在Android画布上有效地绘制了很多元素

时间:2017-02-20 17:21:53

标签: java android multithreading canvas

我正在为Android制作一个互动故事编辑器。故事包含有关作者,其名称和一系列场景的信息:

weight = input ("Enter Weight: ")

if int(weight) <= 2:
    rate = 1.10
elif int(weight) > 2 and int(weight) <= 10:
    rate = 2.20
else:
    rate = 3.70

charge = int(weight)*int(rate)

print (charge)

每个场景都包含有关其背景和一系列事物(元素)的信息:

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); }
}

每件事情都有点复杂:

public class Scene {
    private static final String DEFAULT_B = "#000000";
    public ArrayList<Thing> things;
    public String background;
    Scene(){
        things = new ArrayList<Thing>();
        this.background = DEFAULT_B;
        for(int i = 1; i <= 1000; i++){
            Thing t = new Thing();
                  t.setText("Test " + i);
            add(t);
        }
    }
    public void add(Thing t){ things.add(t); }
}

这是在StoryView上绘制的:

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);

    }
}

从MainActivity调用哪个:

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;
}
}

StoryManager使用Gson从文件加载/保存故事。 问题是,如果要绘制的东西太多(我尝试绘制1000),该应用程序将成为幻灯片,并显示在日志中:

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); } }

I/Choreographer: Skipped 69 frames! The application may be doing too much work on its main thread.

I/Choreographer: Skipped 33 frames! The application may be doing too much work on its main thread.

I/art: Background sticky concurrent mark sweep GC freed 54541(1701KB) AllocSpace objects, 0(0B) LOS objects, 11% free, 13MB/15MB, paused 1.346ms total 100.998ms

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

0 个答案:

没有答案