Java 在Android画布上有效地绘制大量元素
我正在为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
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);
}
}