android遊戲開發框架libgdx的使用(五)–舞臺和常用UI類 – Android移動開發技術文章_手機開發 Android移動開發教學課程

常用的UI類包括標簽,按鈕,勾選框,下拉框,圖片,輸入框,列表,滑動面板,滑條,分割面板。它們都在com.badlogic.gdx.scenes.scene2d.ui包中,都屬於Actor,可以方便的納入舞臺的管理中。

其實仔細看看UI類的實現代碼不難發現其實它們都是大部分繼承自Widget或者Table,如果需要自定義UI可以繼承以上兩個類(它們繼承自Actor),這裡要說明一下libgdx的佈局部分使用瞭TWL,有興趣的朋友可以去看看。

在介紹每個控件之前我們先來看一下NinePatch,這是最近的一個比較重大的更新。

何為NinePatch?其實android原生即有NinePatch類,常在按鈕中使用。

NinePatch

如圖,將圖片分成九份。中間部分可以根據需要擴大,使按鈕的大小內容變動不受圖片的限制。

而在libgdx的NinePatch其實就是九個TextureRegion對象。

常用的實例化方法有兩個:

 


public NinePatch (Texture texture, int left, int right, int top, int bottom) 

 

public NinePatch (TextureRegion region, int left, int right, int top, int bottom)

關於其中的四個int型參數如何取值我們可以參考一下源碼:


public NinePatch (TextureRegion region, int left, int right, int top, int bottom) {  

        int middleWidth = region.getRegionWidth() – left – right;  

        int middleHeight = region.getRegionHeight() – top – bottom;  

        this.patches = new TextureRegion[] {new TextureRegion(region, 0, 0, left, top),  

            new TextureRegion(region, left, 0, middleWidth, top), new TextureRegion(region, left + middleWidth, 0, right, top),  

            new TextureRegion(region, 0, top, left, middleHeight), new TextureRegion(region, left, top, middleWidth, middleHeight),  

            new TextureRegion(region, left + middleWidth, top, right, middleHeight),  

            new TextureRegion(region, 0, top + middleHeight, left, bottom),  

            new TextureRegion(region, left, top + middleHeight, middleWidth, bottom),  

            new TextureRegion(region, left + middleWidth, top + middleHeight, right, bottom)};  

    }

先計算中間部分的寬度和高度。然後開始切圖,首先取頂部的最左邊的那個,即圖中編號1的那塊,然後去它右邊的,然後再右邊的。

取完最上邊的那行,然後取中間的那行,然後取最後一行的。

由上自下,由左自右。

而在繪制時又是如何處理的呢?看源碼:


public void draw (SpriteBatch batch, float x, float y, float width, float height) {  

        float centerColumnX = x;  

        if (patches[BOTTOM_LEFT] != null)  

            centerColumnX += patches[BOTTOM_LEFT].getRegionWidth();  

        else if (patches[MIDDLE_LEFT] != null)  

            centerColumnX += patches[MIDDLE_LEFT].getRegionWidth();  

        else if (patches[TOP_LEFT] != null) //  

            centerColumnX += patches[TOP_LEFT].getRegionWidth(); 

 

        float rightColumnX = x + width;  

        if (patches[BOTTOM_RIGHT] != null)  

            rightColumnX -= patches[BOTTOM_RIGHT].getRegionWidth();  

        else if (patches[MIDDLE_RIGHT] != null)  

            rightColumnX += patches[MIDDLE_RIGHT].getRegionWidth();  

        else if (patches[TOP_RIGHT] != null) //  

            rightColumnX += patches[TOP_RIGHT].getRegionWidth(); 

 

        float middleRowY = y;  

        if (patches[TOP_LEFT] != null)  

            middleRowY += patches[TOP_LEFT].getRegionHeight();  

        else if (patches[TOP_CENTER] != null)  

            middleRowY += patches[TOP_CENTER].getRegionHeight();  

        else if (patches[TOP_RIGHT] != null) //  

            middleRowY += patches[TOP_RIGHT].getRegionHeight(); 

 

        float topRowY = y + height;  

        if (patches[TOP_LEFT] != null)  

            topRowY -= patches[TOP_LEFT].getRegionHeight();  

        else if (patches[TOP_CENTER] != null)  

            topRowY -= patches[TOP_CENTER].getRegionHeight();  

        else if (patches[TOP_RIGHT] != null) //  

            topRowY -= patches[TOP_RIGHT].getRegionHeight(); 

 

        // Bottom row  

        if (patches[BOTTOM_LEFT] != null) batch.draw(patches[BOTTOM_LEFT], x, y, centerColumnX – x, middleRowY – y);  

        if (patches[BOTTOM_CENTER] != null)  

            batch.draw(patches[BOTTOM_CENTER], centerColumnX, y, rightColumnX – centerColumnX, middleRowY – y);  

        if (patches[BOTTOM_RIGHT] != null)  

            batch.draw(patches[BOTTOM_RIGHT], rightColumnX, y, x + width – rightColumnX, middleRowY – y); 

 

        // Middle row  

        if (patches[MIDDLE_LEFT] != null) batch.draw(patches[MIDDLE_LEFT], x, middleRowY, centerColumnX – x, topRowY – middleRowY);  

        if (patches[MIDDLE_CENTER] != null)  

            batch.draw(patches[MIDDLE_CENTER], centerColumnX, middleRowY, rightColumnX – centerColumnX, topRowY – middleRowY);  

        if (patches[MIDDLE_RIGHT] != null)  

            batch.draw(patches[MIDDLE_RIGHT], rightColumnX, middleRowY, x + width – rightColumnX, topRowY – middleRowY); 

 

        // Top row  

        if (patches[TOP_LEFT] != null) batch.draw(patches[TOP_LEFT], x, topRowY, centerColumnX – x, y + height – topRowY);  

        if (patches[TOP_CENTER] != null)  

            batch.draw(patches[TOP_CENTER], centerColumnX, topRowY, rightColumnX – centerColumnX, y + height – topRowY);  

        if (patches[TOP_RIGHT] != null)  

            batch.draw(patches[TOP_RIGHT], rightColumnX, topRowY, x + width – rightColumnX, y + height – topRowY);  

    }

先計算左右欄的寬度,在計算中間和頂部的高度。然後從下自上的繪制。說實話我覺得這段代碼看著很好玩的。

現在來說說幾個常用的控件的使用吧。先構建一個舞臺。

先來試試Label吧,label是有緩存的,所以替換顯示內容不是用setText方法,而是使用setWrappedText方法。

代碼如下:


package com.cnblogs.htynkn.listener; 

 

import com.badlogic.gdx.ApplicationListener;  

import com.badlogic.gdx.Gdx;  

import com.badlogic.gdx.graphics.GL10;  

import com.badlogic.gdx.graphics.g2d.BitmapFont;  

import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;  

import com.badlogic.gdx.scenes.scene2d.Stage;  

import com.badlogic.gdx.scenes.scene2d.actors.Label; 

 

public class FirstGame implements ApplicationListener { 

 

    private Stage stage;  

    Label label; 

 

    @Override

    public void create() {  

        stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),  

                true);  

        label = new Label("fpsLabel", new BitmapFont(Gdx.files.internal("cf.fnt"),Gdx.files.internal("cf.png"),false), "label1");  

        label.x=5;  

        label.y=Gdx.graphics.getHeight()-label.height-5;  

        stage.addActor(label);  

        Gdx.input.setInputProcessor(stage);  

    } 

 

    @Override

    public void dispose() {  

        stage.dispose();  

    } 

 

    @Override

    public void pause() {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void render() {  

        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);  

        label.setWrappedText("FPS: "+Gdx.graphics.getFramesPerSecond(),  

                HAlignment.CENTER);  

        stage.act(Gdx.graphics.getDeltaTime());  

        stage.draw();  

    } 

 

    @Override

    public void resize(int width, int height) {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void resume() {  

        // TODO Auto-generated method stub 

 

    }  

}

 

 

 

效果:

FPS

然後再看看Button吧,實例化需要一個ButtonStyle,定義瞭按鈕三種狀態對應的圖片樣式,按下和松開時的X,Y偏移還有Button中文字繪制所需的BitmapFont和Color。

按鈕的三種狀態的圖片我就省瞭,隻用一張圖片。

06

修改代碼如下:

 


package com.cnblogs.htynkn.listener; 

 

import com.badlogic.gdx.ApplicationListener;  

import com.badlogic.gdx.Gdx;  

import com.badlogic.gdx.graphics.Color;  

import com.badlogic.gdx.graphics.GL10;  

import com.badlogic.gdx.graphics.Texture;  

import com.badlogic.gdx.graphics.g2d.BitmapFont;  

import com.badlogic.gdx.graphics.g2d.NinePatch;  

import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;  

import com.badlogic.gdx.scenes.scene2d.Stage;  

import com.badlogic.gdx.scenes.scene2d.actors.Label;  

import com.badlogic.gdx.scenes.scene2d.ui.Button;  

import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle; 

 

public class FirstGame implements ApplicationListener { 

 

    private Stage stage;  

    Label label;  

    Texture texture;  

    Button button; 

 

    @Override

    public void create() {  

        stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),  

                true);  

        texture = new Texture(Gdx.files.internal("06.png"));  

        NinePatch n1 = new NinePatch(texture, 7, 7, 9, 9);  

        BitmapFont bitmapFont = new BitmapFont(Gdx.files.internal("cf.fnt"),  

                Gdx.files.internal("cf.png"), false);  

        label = new Label("fpsLabel", bitmapFont, "label1");  

        label.x = 5;  

        label.y = Gdx.graphics.getHeight() – label.height – 5;  

        stage.addActor(label);  

        button = new Button("button", new ButtonStyle(n1, n1, n1, 0f, 0f, 0f,  

                0f, bitmapFont, new Color(1, 1, 0, 0.5f)), "button");  

        button.x=10;  

        button.y=10;  

        button.width=100f;  

        button.height=32f;  

        stage.addActor(button);  

        Gdx.input.setInputProcessor(stage);  

    } 

 

    @Override

    public void dispose() {  

        stage.dispose();  

    } 

 

    @Override

    public void pause() {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void render() {  

        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);  

        label.setWrappedText("FPS: " + Gdx.graphics.getFramesPerSecond(),  

                HAlignment.CENTER);  

        stage.act(Gdx.graphics.getDeltaTime());  

        stage.draw();  

    } 

 

    @Override

    public void resize(int width, int height) {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void resume() {  

        // TODO Auto-generated method stub 

 

    }  

}

 

 

 

效果:

libgdx按鈕

按鈕自然應該有點擊事件,通過setClickListener來設置

 

 


button.setClickListener(new ClickListener() {  

            @Override

            public void click(Actor actor) {  

                Gdx.app.log("Info", "點擊事件觸發瞭");  

            }  

        });

然後再看看CheckBox

 

。CheckBox的樣式定義在CheckBoxStyle中,需要4個參數,兩種狀態的各一張圖片,一個BitmapFont和Color。

這裡我再添加一張圖片

07

原理差不多,直接貼代碼瞭。


package com.cnblogs.htynkn.listener; 

 

import android.graphics.Paint.Align; 

 

import com.badlogic.gdx.ApplicationListener;  

import com.badlogic.gdx.Gdx;  

import com.badlogic.gdx.graphics.Color;  

import com.badlogic.gdx.graphics.GL10;  

import com.badlogic.gdx.graphics.Texture;  

import com.badlogic.gdx.graphics.g2d.BitmapFont;  

import com.badlogic.gdx.graphics.g2d.NinePatch;  

import com.badlogic.gdx.graphics.g2d.TextureRegion;  

import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;  

import com.badlogic.gdx.scenes.scene2d.Actor;  

import com.badlogic.gdx.scenes.scene2d.Stage;  

import com.badlogic.gdx.scenes.scene2d.actors.Label;  

import com.badlogic.gdx.scenes.scene2d.ui.Button;  

import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;  

import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;  

import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;  

import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle; 

 

public class FirstGame implements ApplicationListener { 

 

    private Stage stage;  

    Label label;  

    Texture texture1;  

    Texture texture2;  

    CheckBox checkBox; 

 

    @Override

    public void create() {  

        stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),  

                true);  

        texture1 = new Texture(Gdx.files.internal("06.png"));  

        texture2 = new Texture(Gdx.files.internal("07.png"));  

        NinePatch n1 = new NinePatch(texture1, 7, 7, 9, 9);  

        BitmapFont bitmapFont = new BitmapFont(Gdx.files.internal("cf.fnt"),  

                Gdx.files.internal("cf.png"), false);  

        label = new Label("fpsLabel", bitmapFont, "label1");  

        label.x = 5;  

        label.y = Gdx.graphics.getHeight() – label.height – 5;  

        CheckBoxStyle style = new CheckBoxStyle(new TextureRegion(texture1),  

                new TextureRegion(texture2), bitmapFont, new Color(1, 1, 1,  

                        0.5f)); 

 

        checkBox = new CheckBox("checkbox", style, "checkbox");  

        checkBox.x = 100;  

        checkBox.y = 100;  

        checkBox.width = 158f;  

        checkBox.height = 32f;  

        checkBox.setText("Yes");  

        checkBox.setClickListener(new ClickListener() { 

 

            @Override

            public void click(Actor actor) {  

                if (checkBox.isChecked) {  

                    checkBox.setText("Yes");  

                } else {  

                    checkBox.setText("NO");  

                }  

            }  

        });  

        stage.addActor(checkBox);  

        stage.addActor(label);  

        Gdx.input.setInputProcessor(stage);  

    } 

 

    @Override

    public void dispose() {  

        stage.dispose();  

    } 

 

    @Override

    public void pause() {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void render() {  

        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);  

        label.setWrappedText("FPS: " + Gdx.graphics.getFramesPerSecond(),  

                HAlignment.CENTER);  

        stage.act(Gdx.graphics.getDeltaTime());  

        stage.draw();  

    } 

 

    @Override

    public void resize(int width, int height) {  

        // TODO Auto-generated method stub 

 

    } 

 

    @Override

    public void resume() {  

        // TODO Auto-generated method stub 

 

    }  

}

效果:

noyes

其他的UI大致用法差不多,顯示的樣式在對應的Style或者Skin中定義。但是要註意有些UI類需要手動設置width和height,不然有些顯示會很奇怪的。

最後說一下Slider的用法。

SliderStyle需要一個NinePath和Texture,我最初沒有想通為什麼不是兩個NinePath,仔細看一下源碼才瞭解到,NinePath是作為背景,而Texture那個是中間的那個滑動的方塊。

關於用配置文件設置Style的問題,google code的wiki上似乎沒有寫,但是在libgdx的論壇裡面有,比如

		somePatch1: [
    { height: 13, width: 9, x: 761, y: 78 },
    { height: 13, width: 1, x: 770, y: 78 },
    { height: 13, width: 9, x: 771, y: 78 },
    { height: 1, width: 9, x: 761, y: 91 },
    { height: 1, width: 1, x: 770, y: 91 },
    { height: 1, width: 9, x: 771, y: 91 },
    { height: 13, width: 9, x: 761, y: 92 },
    { height: 13, width: 1, x: 770, y: 92 },
    { height: 13, width: 9, x: 771, y: 92 }
]

	或者

		somePatch2: [
    { height: 13, width: 9, x: 761, y: 78 },
]


作者:黃雲坤
出處:http://www.cnblogs.com/htynkn/

發佈留言