Java 在Android画布上有效地绘制大量元素

Java 在Android画布上有效地绘制大量元素,java,android,multithreading,canvas,Java,Android,Multithreading,Canvas,我正在为Android制作一个交互式故事编辑器。该故事包含有关作者、作者姓名和一系列场景的信息: public class Story { public ArrayList<Scene> scenes; private String author; private String name; public Story(){ scenes = new ArrayList<Scene>(); Scene s = n

我正在为Android制作一个交互式故事编辑器。该故事包含有关作者、作者姓名和一系列场景的信息:

public class Story {
    public ArrayList<Scene> scenes;
    private String author;
    private String name;
    public Story(){
        scenes = new ArrayList<Scene>();
        Scene s = new Scene(); /* Initializing a new story adds a first scene to it. */
        this.add(s);
    }
    public void add(Scene s){ scenes.add(s); }
}
从MainActivity调用的:

public class MainActivity extends AppCompatActivity {
    StoryView storyView;
    public static MainActivity instance;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }
    private void init(){
        instance = this;
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        storyView = new StoryView(this);
        setContentView(storyView);

        StoryManager.deleteStory("test");
        StoryManager.createStory("test");
        Story test = StoryManager.loadStory("test");

        storyView.play(test);

    }
}
StoryManager使用Gson从文件加载/保存故事。 问题是,如果有太多的东西要画(我试着画了1000),应用程序就会变成幻灯片,并显示在日志中:

I/编舞:跳过69帧!应用程序可能在其主线程上做了太多工作。

I/编舞:跳过33帧!应用程序可能在其主线程上做了太多工作。

I/art:背景粘性并发标记扫描GC释放54541(1701KB)AllocSpace对象,0(0B)LOS对象,11%空闲,13MB/15MB,暂停1.346ms总计100.998ms

I/编舞:跳过251帧!应用程序可能在其主线程上做了太多工作。

也许有更有效的方法来渲染这些东西?如果有100件事情,我没有错误和延迟。
编辑:编舞信息仍然出现在100件事上。

如果性能是您的问题,请使用LibGDX。不要使用Android画布。@pepan我不需要使用大多数LibGDX功能:交互式故事将只包含文本、图像和颜色。如果性能是您的问题,请使用LibGDX。不要使用Android画布。@pepan我不需要使用大部分LibGDX功能:交互式故事将只包含文本、图像和颜色。
public class Thing {

    /* constants removed */

    private float w;
    private float h;
    private float x;
    private float y;

    private String background_color;
    private String text_color;
    private String text;

    Thing(){
        /* initialize with constants */
    }

    /* getters and setters removed */

    public void onTouch(double x, double y){
        Toast.makeText(MainActivity.instance, "Clicked on Thing '" + this.text + "'", Toast.LENGTH_SHORT).show();
    }

    public RectF box(){
        return new RectF(x, y, x+w, y+h);
    }

    public int resizeFrom(float x, float y){ /* try resizing */
        for(RectF r: getResizerRects()){
            if(inflatedRect(r, 15).contains(x, y)) return getResizerRects().indexOf(r);
        }
        return -1;
    }

    public void render(Canvas canvas, boolean sel){
        Paint p = new Paint();
        p.setAntiAlias(true);
        RectF r = box();
        p.setColor(Color.parseColor(background_color)); //todo res parser
        canvas.drawRect(r, p);

        p.setColor(Color.parseColor(text_color));
        float font_size = 30.0f;
        p.setTextSize(font_size);
        Rect b = new Rect();
        p.getTextBounds(text, 0, text.length(), b);
        canvas.drawText(text, x + 10, y + 35, p);


        p.setStyle(Paint.Style.STROKE);
        p.setStrokeWidth(2);
        p.setColor(Color.BLACK);
        canvas.drawRect(r, p);

        if(sel) drawResizers(canvas);
    }
    private ArrayList<RectF> getResizerRects(){
        ArrayList<RectF> a = new ArrayList<>();
            a.add(centerRect(x, y));
            a.add(centerRect(x+w/2, y));
            a.add(centerRect(x+w, y));
            a.add(centerRect(x, y+h/2));
            a.add(centerRect(x+w, y+h/2));
            a.add(centerRect(x, y+h));
            a.add(centerRect(x+w/2, y+h));
            a.add(centerRect(x+w, y+h));
        return a;
    }
    private RectF inflatedRect(RectF r, float d){
        return new RectF(r.left - d, r.top - d, r.right + d*2, r.bottom + d*2);
    }
    private void drawResizers(Canvas c){
        for(RectF r: getResizerRects()) drawResizer(c, r);
    }
    private RectF centerRect(float x, float y){
        final float size = 14.0f;
        return new RectF(x - size/2, y - size/2, x+size/2, y+size/2);
    }
    private void drawResizer(Canvas canvas, RectF r){
        Paint paint = new Paint();

        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);
        canvas.drawRect(r, paint);

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.BLACK);
        canvas.drawRect(r, paint);

    }
}
public class StoryView extends View{

private Story story;
private int scene;
private int selected_thing;
private int resize_corner;
private PointF click;

private Paint p;
private int width;
private int height;

public StoryView(Context context) {
    super(context);

    p = new Paint();
    p.setAntiAlias(true);
    this.story = null;
    this.scene = 0;
    this.selected_thing = -1;
    this.resize_corner = -1;
    this.click = new PointF();

    this.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            StoryView.this.onTouch(event.getX(), event.getY(), event.getAction());
            return true;
        }
    });
}

private void onTouch(float x, float y, int event){
    Scene s = this.story.scenes.get(scene);
    if(event == MotionEvent.ACTION_DOWN) {
        boolean was_selected = false;
        if(selected_thing != -1) resize_corner = s.things.get(selected_thing).resizeFrom(x, y);
        if(resize_corner == -1) {
            for (Thing t : s.things) {
                if (t.box().contains(x, y)) {
                    t.onTouch(x, y);
                    was_selected = true;
                    selected_thing = s.things.indexOf(t);
                    click.x = x - t.getPosition().x;
                    click.y = y - t.getPosition().y;
                }

            }
        }

        if(!was_selected && resize_corner == -1)  selected_thing = -1;

    }
    else if(event == MotionEvent.ACTION_MOVE){
        if(selected_thing != -1){
            if(resize_corner != -1){ // resize
                if(resize_corner == 0){
                    RectF box = s.things.get(selected_thing).box();
                    box.left = x;
                    box.top = y;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 1){
                    RectF box = s.things.get(selected_thing).box();
                    box.top = y;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 2){
                    RectF box = s.things.get(selected_thing).box();
                    box.right = x;
                    box.top = y;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 3){
                    RectF box = s.things.get(selected_thing).box();
                    box.left = x;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 4){
                    RectF box = s.things.get(selected_thing).box();
                    box.right = x;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 5){
                    RectF box = s.things.get(selected_thing).box();
                    box.left = x;
                    box.bottom = y;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 6){
                    RectF box = s.things.get(selected_thing).box();
                    box.bottom = y;
                    s.things.get(selected_thing).setBox(box);
                }
                else if(resize_corner == 7){
                    RectF box = s.things.get(selected_thing).box();
                    box.right = x;
                    box.bottom = y;
                    s.things.get(selected_thing).setBox(box);
                }
            }
            else s.things.get(selected_thing).setPosition(x-click.x, y-click.y); // move
        }
    }
    else if(event == MotionEvent.ACTION_UP){
        resize_corner = -1;
    }
}

public void play(Story s){
    this.story = s;
}
protected void onDraw(Canvas canvas){
    super.onDraw(canvas);

    Scene s = this.story.scenes.get(scene);

    canvas.drawColor(Color.parseColor(s.background)); // todo: add a resource parser: background can be image or color
    for (Thing t: s.things){
        boolean sel = s.things.indexOf(t) == selected_thing;
        t.render(canvas, sel);
    }


    update();
}

protected void update(){

    invalidate();
}

protected void onSizeChanged(int w, int h, int _w, int _h){
    super.onSizeChanged(w, h, _w, _h);
    width = w;
    height = h;
}
}
public class MainActivity extends AppCompatActivity {
    StoryView storyView;
    public static MainActivity instance;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }
    private void init(){
        instance = this;
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        storyView = new StoryView(this);
        setContentView(storyView);

        StoryManager.deleteStory("test");
        StoryManager.createStory("test");
        Story test = StoryManager.loadStory("test");

        storyView.play(test);

    }
}