android游戏开发框架libgdx的使用(五)--舞台和常用UI类

转载声明:http://www.cnblogs.com/htynkn/archive/2011/11/16/libgdx_5.html

常用的UI类包括标签,按钮,勾选框,下拉框,图片,输入框,列表,滑动面板,滑条,分割面板。它们都在com.badlogic.gdx.scenes.scene2d.ui包中,都属于Actor,可以方便的纳入舞台的管理中。

其实仔细看看UI类的实现代码不难发现其实它们都是大部分继承自Widget或者Table,如果需要自定义UI可以继承以上两个类(它们继承自Actor),这里要说明一下libgdx的布局部分使用了TWL,有兴趣的朋友可以去看看。

在介绍每个控件之前我们先来看一下NinePatch,这是最近的一个比较重大的更新。

何为NinePatch?其实android原生即有NinePatch类,常在按钮中使用。

NinePatch

如图,将图片分成九份。中间部分可以根据需要扩大,使按钮的大小内容变动不受图片的限制。

而在libgdx的NinePatch其实就是九个TextureRegion对象。

常用的实例化方法有两个:

1 public NinePatch (Texture texture, int left, int right, int top, int bottom)
2  
3 public NinePatch (TextureRegion region, int left, int right, int top, int bottom)

关于其中的四个int型参数如何取值我们可以参考一下源码:

 1 public NinePatch (TextureRegion region, int left, int right, int top, int bottom) {
 2         int middleWidth = region.getRegionWidth() - left - right;
 3         int middleHeight = region.getRegionHeight() - top - bottom;
 4         this.patches = new TextureRegion[] {new TextureRegion(region, 0, 0, left, top),
 5             new TextureRegion(region, left, 0, middleWidth, top), new TextureRegion(region, left + middleWidth, 0, right, top),
 6             new TextureRegion(region, 0, top, left, middleHeight), new TextureRegion(region, left, top, middleWidth, middleHeight),
 7             new TextureRegion(region, left + middleWidth, top, right, middleHeight),
 8             new TextureRegion(region, 0, top + middleHeight, left, bottom),
 9             new TextureRegion(region, left, top + middleHeight, middleWidth, bottom),
10             new TextureRegion(region, left + middleWidth, top + middleHeight, right, bottom)};
11     }

先计算中间部分的宽度和高度。然后开始切图,首先取顶部的最左边的那个,即图中编号1的那块,然后去它右边的,然后再右边的。

取完最上边的那行,然后取中间的那行,然后取最后一行的。

由上自下,由左自右。

而在绘制时又是如何处理的呢?看源码:

 1 public void draw (SpriteBatch batch, float x, float y, float width, float height) {
 2         float centerColumnX = x;
 3         if (patches[BOTTOM_LEFT] != null)
 4             centerColumnX += patches[BOTTOM_LEFT].getRegionWidth();
 5         else if (patches[MIDDLE_LEFT] != null)
 6             centerColumnX += patches[MIDDLE_LEFT].getRegionWidth();
 7         else if (patches[TOP_LEFT] != null) //
 8             centerColumnX += patches[TOP_LEFT].getRegionWidth();
 9  
10         float rightColumnX = x + width;
11         if (patches[BOTTOM_RIGHT] != null)
12             rightColumnX -= patches[BOTTOM_RIGHT].getRegionWidth();
13         else if (patches[MIDDLE_RIGHT] != null)
14             rightColumnX += patches[MIDDLE_RIGHT].getRegionWidth();
15         else if (patches[TOP_RIGHT] != null) //
16             rightColumnX += patches[TOP_RIGHT].getRegionWidth();
17  
18         float middleRowY = y;
19         if (patches[TOP_LEFT] != null)
20             middleRowY += patches[TOP_LEFT].getRegionHeight();
21         else if (patches[TOP_CENTER] != null)
22             middleRowY += patches[TOP_CENTER].getRegionHeight();
23         else if (patches[TOP_RIGHT] != null) //
24             middleRowY += patches[TOP_RIGHT].getRegionHeight();
25  
26         float topRowY = y + height;
27         if (patches[TOP_LEFT] != null)
28             topRowY -= patches[TOP_LEFT].getRegionHeight();
29         else if (patches[TOP_CENTER] != null)
30             topRowY -= patches[TOP_CENTER].getRegionHeight();
31         else if (patches[TOP_RIGHT] != null) //
32             topRowY -= patches[TOP_RIGHT].getRegionHeight();
33  
34         // Bottom row
35         if (patches[BOTTOM_LEFT] != null) batch.draw(patches[BOTTOM_LEFT], x, y, centerColumnX - x, middleRowY - y);
36         if (patches[BOTTOM_CENTER] != null)
37             batch.draw(patches[BOTTOM_CENTER], centerColumnX, y, rightColumnX - centerColumnX, middleRowY - y);
38         if (patches[BOTTOM_RIGHT] != null)
39             batch.draw(patches[BOTTOM_RIGHT], rightColumnX, y, x + width - rightColumnX, middleRowY - y);
40  
41         // Middle row
42         if (patches[MIDDLE_LEFT] != null) batch.draw(patches[MIDDLE_LEFT], x, middleRowY, centerColumnX - x, topRowY - middleRowY);
43         if (patches[MIDDLE_CENTER] != null)
44             batch.draw(patches[MIDDLE_CENTER], centerColumnX, middleRowY, rightColumnX - centerColumnX, topRowY - middleRowY);
45         if (patches[MIDDLE_RIGHT] != null)
46             batch.draw(patches[MIDDLE_RIGHT], rightColumnX, middleRowY, x + width - rightColumnX, topRowY - middleRowY);
47  
48         // Top row
49         if (patches[TOP_LEFT] != null) batch.draw(patches[TOP_LEFT], x, topRowY, centerColumnX - x, y + height - topRowY);
50         if (patches[TOP_CENTER] != null)
51             batch.draw(patches[TOP_CENTER], centerColumnX, topRowY, rightColumnX - centerColumnX, y + height - topRowY);
52         if (patches[TOP_RIGHT] != null)
53             batch.draw(patches[TOP_RIGHT], rightColumnX, topRowY, x + width - rightColumnX, y + height - topRowY);
54     }

先计算左右栏的宽度,在计算中间和顶部的高度。然后从下自上的绘制。说实话我觉得这段代码看着很好玩的。

现在来说说几个常用的控件的使用吧。先构建一个舞台。

先来试试Label吧,label是有缓存的,所以替换显示内容不是用setText方法,而是使用setWrappedText方法。

代码如下:

 1 package com.cnblogs.htynkn.listener;
 2  
 3 import com.badlogic.gdx.ApplicationListener;
 4 import com.badlogic.gdx.Gdx;
 5 import com.badlogic.gdx.graphics.GL10;
 6 import com.badlogic.gdx.graphics.g2d.BitmapFont;
 7 import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
 8 import com.badlogic.gdx.scenes.scene2d.Stage;
 9 import com.badlogic.gdx.scenes.scene2d.actors.Label;
10  
11 public class FirstGame implements ApplicationListener {
12  
13     private Stage stage;
14     Label label;
15  
16     @Override
17     public void create() {
18         stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),
19                 true);
20         label = new Label("fpsLabel", new BitmapFont(Gdx.files.internal("cf.fnt"),Gdx.files.internal("cf.png"),false), "label1");
21         label.x=5;
22         label.y=Gdx.graphics.getHeight()-label.height-5;
23         stage.addActor(label);
24         Gdx.input.setInputProcessor(stage);
25     }
26  
27     @Override
28     public void dispose() {
29         stage.dispose();
30     }
31  
32     @Override
33     public void pause() {
34         // TODO Auto-generated method stub
35  
36     }
37  
38     @Override
39     public void render() {
40         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
41         label.setWrappedText("FPS: "+Gdx.graphics.getFramesPerSecond(),
42                 HAlignment.CENTER);
43         stage.act(Gdx.graphics.getDeltaTime());
44         stage.draw();
45     }
46  
47     @Override
48     public void resize(int width, int height) {
49         // TODO Auto-generated method stub
50  
51     }
52  
53     @Override
54     public void resume() {
55         // TODO Auto-generated method stub
56  
57     }
58 }

效果:

FPS

然后再看看Button吧,实例化需要一个ButtonStyle,定义了按钮三种状态对应的图片样式,按下和松开时的X,Y偏移还有Button中文字绘制所需的BitmapFont和Color。

按钮的三种状态的图片我就省了,只用一张图片。

06

修改代码如下:

 1 package com.cnblogs.htynkn.listener;
 2  
 3 import com.badlogic.gdx.ApplicationListener;
 4 import com.badlogic.gdx.Gdx;
 5 import com.badlogic.gdx.graphics.Color;
 6 import com.badlogic.gdx.graphics.GL10;
 7 import com.badlogic.gdx.graphics.Texture;
 8 import com.badlogic.gdx.graphics.g2d.BitmapFont;
 9 import com.badlogic.gdx.graphics.g2d.NinePatch;
10 import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
11 import com.badlogic.gdx.scenes.scene2d.Stage;
12 import com.badlogic.gdx.scenes.scene2d.actors.Label;
13 import com.badlogic.gdx.scenes.scene2d.ui.Button;
14 import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
15  
16 public class FirstGame implements ApplicationListener {
17  
18     private Stage stage;
19     Label label;
20     Texture texture;
21     Button button;
22  
23     @Override
24     public void create() {
25         stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),
26                 true);
27         texture = new Texture(Gdx.files.internal("06.png"));
28         NinePatch n1 = new NinePatch(texture, 7, 7, 9, 9);
29         BitmapFont bitmapFont = new BitmapFont(Gdx.files.internal("cf.fnt"),
30                 Gdx.files.internal("cf.png"), false);
31         label = new Label("fpsLabel", bitmapFont, "label1");
32         label.x = 5;
33         label.y = Gdx.graphics.getHeight() - label.height - 5;
34         stage.addActor(label);
35         button = new Button("button", new ButtonStyle(n1, n1, n1, 0f, 0f, 0f,
36                 0f, bitmapFont, new Color(1, 1, 0, 0.5f)), "button");
37         button.x=10;
38         button.y=10;
39         button.width=100f;
40         button.height=32f;
41         stage.addActor(button);
42         Gdx.input.setInputProcessor(stage);
43     }
44  
45     @Override
46     public void dispose() {
47         stage.dispose();
48     }
49  
50     @Override
51     public void pause() {
52         // TODO Auto-generated method stub
53  
54     }
55  
56     @Override
57     public void render() {
58         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
59         label.setWrappedText("FPS: " + Gdx.graphics.getFramesPerSecond(),
60                 HAlignment.CENTER);
61         stage.act(Gdx.graphics.getDeltaTime());
62         stage.draw();
63     }
64  
65     @Override
66     public void resize(int width, int height) {
67         // TODO Auto-generated method stub
68  
69     }
70  
71     @Override
72     public void resume() {
73         // TODO Auto-generated method stub
74  
75     }
76 }

效果:

libgdx按钮

按钮自然应该有点击事件,通过setClickListener来设置

1 button.setClickListener(new ClickListener() {
2             @Override
3             public void click(Actor actor) {
4                 Gdx.app.log("Info", "点击事件触发了");
5             }
6         });

然后再看看CheckBox。CheckBox的样式定义在CheckBoxStyle中,需要4个参数,两种状态的各一张图片,一个BitmapFont和Color。

这里我再添加一张图片

07

原理差不多,直接贴代码了。

  1 package com.cnblogs.htynkn.listener;
  2  
  3 import android.graphics.Paint.Align;
  4  
  5 import com.badlogic.gdx.ApplicationListener;
  6 import com.badlogic.gdx.Gdx;
  7 import com.badlogic.gdx.graphics.Color;
  8 import com.badlogic.gdx.graphics.GL10;
  9 import com.badlogic.gdx.graphics.Texture;
 10 import com.badlogic.gdx.graphics.g2d.BitmapFont;
 11 import com.badlogic.gdx.graphics.g2d.NinePatch;
 12 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 13 import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
 14 import com.badlogic.gdx.scenes.scene2d.Actor;
 15 import com.badlogic.gdx.scenes.scene2d.Stage;
 16 import com.badlogic.gdx.scenes.scene2d.actors.Label;
 17 import com.badlogic.gdx.scenes.scene2d.ui.Button;
 18 import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
 19 import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;
 20 import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
 21 import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle;
 22  
 23 public class FirstGame implements ApplicationListener {
 24  
 25     private Stage stage;
 26     Label label;
 27     Texture texture1;
 28     Texture texture2;
 29     CheckBox checkBox;
 30  
 31     @Override
 32     public void create() {
 33         stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),
 34                 true);
 35         texture1 = new Texture(Gdx.files.internal("06.png"));
 36         texture2 = new Texture(Gdx.files.internal("07.png"));
 37         NinePatch n1 = new NinePatch(texture1, 7, 7, 9, 9);
 38         BitmapFont bitmapFont = new BitmapFont(Gdx.files.internal("cf.fnt"),
 39                 Gdx.files.internal("cf.png"), false);
 40         label = new Label("fpsLabel", bitmapFont, "label1");
 41         label.x = 5;
 42         label.y = Gdx.graphics.getHeight() - label.height - 5;
 43         CheckBoxStyle style = new CheckBoxStyle(new TextureRegion(texture1),
 44                 new TextureRegion(texture2), bitmapFont, new Color(1, 1, 1,
 45                         0.5f));
 46  
 47         checkBox = new CheckBox("checkbox", style, "checkbox");
 48         checkBox.x = 100;
 49         checkBox.y = 100;
 50         checkBox.width = 158f;
 51         checkBox.height = 32f;
 52         checkBox.setText("Yes");
 53         checkBox.setClickListener(new ClickListener() {
 54  
 55             @Override
 56             public void click(Actor actor) {
 57                 if (checkBox.isChecked) {
 58                     checkBox.setText("Yes");
 59                 } else {
 60                     checkBox.setText("NO");
 61                 }
 62             }
 63         });
 64         stage.addActor(checkBox);
 65         stage.addActor(label);
 66         Gdx.input.setInputProcessor(stage);
 67     }
 68  
 69     @Override
 70     public void dispose() {
 71         stage.dispose();
 72     }
 73  
 74     @Override
 75     public void pause() {
 76         // TODO Auto-generated method stub
 77  
 78     }
 79  
 80     @Override
 81     public void render() {
 82         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 83         label.setWrappedText("FPS: " + Gdx.graphics.getFramesPerSecond(),
 84                 HAlignment.CENTER);
 85         stage.act(Gdx.graphics.getDeltaTime());
 86         stage.draw();
 87     }
 88  
 89     @Override
 90     public void resize(int width, int height) {
 91         // TODO Auto-generated method stub
 92  
 93     }
 94  
 95     @Override
 96     public void resume() {
 97         // TODO Auto-generated method stub
 98  
 99     }
100 }

效果:

noyes

其他的UI大致用法差不多,显示的样式在对应的Style或者Skin中定义。但是要注意有些UI类需要手动设置width和height,不然有些显示会很奇怪的。

最后说一下Slider的用法。

SliderStyle需要一个NinePath和Texture,我最初没有想通为什么不是两个NinePath,仔细看一下源码才了解到,NinePath是作为背景,而Texture那个是中间的那个滑动的方块。

关于用配置文件设置Style的问题,google code的wiki上似乎没有写,但是在libgdx的论坛里面有,比如

 1 somePatch1: [
 2     { height: 13, width: 9, x: 761, y: 78 },
 3     { height: 13, width: 1, x: 770, y: 78 },
 4     { height: 13, width: 9, x: 771, y: 78 },
 5     { height: 1, width: 9, x: 761, y: 91 },
 6     { height: 1, width: 1, x: 770, y: 91 },
 7     { height: 1, width: 9, x: 771, y: 91 },
 8     { height: 13, width: 9, x: 761, y: 92 },
 9     { height: 13, width: 1, x: 770, y: 92 },
10     { height: 13, width: 9, x: 771, y: 92 }
11 ]
或者
1 somePatch2: [
2     { height: 13, width: 9, x: 761, y: 78 },
3 ]

 

posted @ 2013-02-04 20:40  王世桢  阅读(205)  评论(0)    收藏  举报