├── README.md ├── assets └── Lantingcuhei.TTF ├── goeasy ├── baseutil │ └── baseutil.go ├── ui │ └── TextViewGo.go ├── uimanager │ └── layoutparam │ │ └── layoutparam.go └── util │ └── util.go ├── main.go └── main_x.go /README.md: -------------------------------------------------------------------------------- 1 | # GoEasy 2 |

使用GoMobile开发native应用,同时编译android与ios应用

3 |

已经完成TextViewGo控件,可直接使用,相当于Android中的TextView

4 |

使用方式SetPosition(x,y);SetSize(width,height);SetDpi();SetTypeFace();SetText();SetTextSize();

5 | 6 |

编译方式android:gomobile build -target=android GoEasy

7 |

安装方式android:gomobile install GoEasy

8 | 9 |

接口并不完善,有兴趣的可以大家一起开发

10 |

目前卡在EditView中弹出输入法,要做到android与ios共用一种方式不知如何下手。

11 | 12 | ### QQ群:186305789 - 疯狂的程序员 13 | ### 个人兴趣网站,[zero接码平台](https://xinghai.party) 14 | ### 个人兴趣网站,[猿指](https://blog.xinghai.party) 15 | -------------------------------------------------------------------------------- /assets/Lantingcuhei.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerochl/GoEasy/bcd9606b3ceab01590f8f522e681a8f13a7801c1/assets/Lantingcuhei.TTF -------------------------------------------------------------------------------- /goeasy/baseutil/baseutil.go: -------------------------------------------------------------------------------- 1 | package baseutil 2 | 3 | func Abs(value int) int { 4 | if value >= 0 { 5 | return value 6 | } else { 7 | return -value 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /goeasy/ui/TextViewGo.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | // 4 | // "image" 5 | // // "image/color" 6 | // "image/png" 7 | // "io/ioutil" 8 | // "os" 9 | import ( 10 | "fmt" 11 | "image" 12 | 13 | "GoEasy/goeasy/uimanager/layoutparam" 14 | 15 | "GoEasy/goeasy/util" 16 | // "../uimanager/layoutparam" 17 | "io/ioutil" 18 | 19 | // "image/color" 20 | "image/draw" 21 | "log" 22 | 23 | "github.com/golang/freetype/truetype" 24 | 25 | // "math" 26 | 27 | "github.com/golang/freetype" 28 | "golang.org/x/mobile/asset" 29 | "golang.org/x/mobile/event/size" 30 | "golang.org/x/mobile/exp/gl/glutil" 31 | "golang.org/x/mobile/geom" 32 | ) 33 | 34 | const ( 35 | // dx = 100 // 图片的大小 宽度 36 | // dy = 40 // 图片的大小 高度 37 | // fontFile = "Lantingcuhei.TTF" // 需要使用的字体文件 38 | // fontSize = 18 // 字体尺寸 39 | // fontDPI = 72 // 屏幕每英寸的分辨率 40 | ) 41 | 42 | type TextViewGo struct { 43 | SizeEntry *layoutparam.SizeEntry 44 | fontFile string 45 | fontSize float64 // 字体尺寸 46 | fontDPI float64 // 屏幕每英寸的分辨率 47 | font *truetype.Font 48 | sz size.Event 49 | freeContext *freetype.Context 50 | text string 51 | image *glutil.Image 52 | X int 53 | Y int 54 | // backgroundColor *Uniform 55 | } 56 | 57 | func (context *TextViewGo) SetSize(width, height int) { 58 | if context.SizeEntry == nil { 59 | context.SizeEntry = layoutparam.NewLayoutParam(width, height) 60 | } else { 61 | context.SizeEntry.SetSize(width, height) 62 | fmt.Println(context.SizeEntry.Width) 63 | } 64 | } 65 | 66 | func (context *TextViewGo) SetPosition(x, y int) { 67 | context.X = x 68 | context.Y = y 69 | } 70 | func (context *TextViewGo) SetX(x int) { 71 | context.X = x 72 | } 73 | func (context *TextViewGo) SetY(y int) { 74 | context.Y = y 75 | } 76 | 77 | func (context *TextViewGo) SetTypeface(fontFilePath string) { 78 | context.fontFile = fontFilePath 79 | // fontBytes, err := ioutil.ReadFile(context.fontFile) 80 | fontFile, err := asset.Open(context.fontFile) 81 | if err != nil { 82 | log.Println(err) 83 | return 84 | } 85 | fontBytes, err := ioutil.ReadAll(fontFile) 86 | if err != nil { 87 | log.Println(err) 88 | return 89 | } 90 | font, err := freetype.ParseFont(fontBytes) 91 | if err != nil { 92 | log.Println(err) 93 | return 94 | } 95 | context.font = font 96 | context.freeContext.SetFont(context.font) 97 | } 98 | 99 | func (context *TextViewGo) SetTextSize(textSize float64) { 100 | context.fontSize = textSize 101 | context.freeContext.SetFontSize(float64(context.fontSize)) 102 | } 103 | 104 | func (context *TextViewGo) SetFontDpi(dpi float64) { 105 | context.fontDPI = dpi 106 | context.freeContext.SetDPI(float64(context.fontDPI)) 107 | } 108 | 109 | func (context *TextViewGo) SetText(text string) { 110 | context.text = text 111 | } 112 | 113 | func (context *TextViewGo) Draw(canvas *glutil.Images, sz size.Event) { 114 | if sz.WidthPx == 0 && sz.HeightPx == 0 || context.font == nil { 115 | log.Println("sz is width ==0 height ==0 or font is nil") 116 | return 117 | } 118 | ptScale := float32(sz.HeightPx) / float32(sz.HeightPt) 119 | scWidth, scHeight, fontSize := int(float32(context.SizeEntry.Width)*ptScale), int(float32(context.SizeEntry.Height)*ptScale), (float64(context.fontSize) * float64(ptScale)) 120 | if context.sz != sz { 121 | context.sz = sz 122 | if context.image != nil { 123 | context.image.Release() 124 | } 125 | context.image = canvas.NewImage(scWidth, scHeight) 126 | } 127 | draw.Draw(context.image.RGBA, context.image.RGBA.Bounds(), image.Black, image.Point{}, draw.Src) 128 | 129 | freeCon := context.freeContext 130 | freeCon.SetClip(context.image.RGBA.Bounds()) 131 | freeCon.SetDst(context.image.RGBA) 132 | freeCon.SetSrc(image.White) 133 | freeCon.SetFontSize(fontSize) 134 | 135 | // pt := freetype.Pt(10, (40-util.GetRealSize(context.fontSize, freeCon))/2+util.GetRealSize(context.fontSize, freeCon)) 136 | // log.Println((40-util.GetRealSize(context.fontSize, freeCon))/2 + util.GetRealSize(context.fontSize, freeCon)) 137 | pt := freetype.Pt(0, 0) 138 | // log.Println((10 + util.GetRealSize(context.fontSize, freeCon))) 139 | left, top := 10, (scHeight-util.GetRealSize(fontSize, freeCon))/2 140 | _, maxOffsetY, _ := util.GetTextLength(freeCon, context.text, pt) 141 | // log.Println("length111111:X:%d;Y:%d", ptest.X, ptest.Y) 142 | _, err := freeCon.DrawString(context.text, pt, maxOffsetY, left, top) 143 | // log.Println("length:X:%d;Y:%d", length.X, length.Y) 144 | if err != nil { 145 | log.Println(err) 146 | return 147 | } 148 | // log.Println("sz:width", sz.HeightPt, sz.HeightPx) 149 | context.image.Upload() 150 | // ptScale := float32(sz.HeightPt) / float32(sz.HeightPx) 151 | // context.image.Draw( 152 | // sz, 153 | // geom.Point{sz.WidthPt - geom.Pt(float32(context.SizeEntry.Width)*ptScale), sz.HeightPt - geom.Pt(float32(context.SizeEntry.Height)*float32(ptScale))}, 154 | // geom.Point{sz.WidthPt, sz.HeightPt - geom.Pt(float32(context.SizeEntry.Height)*ptScale)}, 155 | // geom.Point{sz.WidthPt - geom.Pt(float32(context.SizeEntry.Width)*ptScale), sz.HeightPt}, 156 | // context.image.RGBA.Bounds(), 157 | // ) 158 | log.Println("Y:", context.Y) 159 | context.image.Draw( 160 | sz, 161 | geom.Point{geom.Pt(context.X), geom.Pt(context.Y)}, 162 | geom.Point{geom.Pt(context.SizeEntry.Width + context.X), geom.Pt(context.Y)}, 163 | geom.Point{geom.Pt(context.X), geom.Pt(context.Y + context.SizeEntry.Height)}, 164 | context.image.RGBA.Bounds(), 165 | ) 166 | } 167 | 168 | func NewTextView() *TextViewGo { 169 | context := &TextViewGo{ 170 | fontSize: 12, 171 | fontDPI: 72, 172 | X: 0, 173 | Y: 0, 174 | freeContext: freetype.NewContext(), 175 | } 176 | context.SetTypeface(util.NormalFontFile) 177 | context.freeContext.SetDPI(float64(context.fontDPI)) 178 | context.freeContext.SetFont(context.font) 179 | context.freeContext.SetFontSize(float64(context.fontSize)) 180 | return context 181 | } 182 | 183 | func NewTextViewWithSize(sizeEntry *layoutparam.SizeEntry) *TextViewGo { 184 | context := &TextViewGo{ 185 | SizeEntry: sizeEntry, 186 | fontSize: 12, 187 | fontDPI: 72, 188 | X: 0, 189 | Y: 0, 190 | freeContext: freetype.NewContext(), 191 | } 192 | context.SetTypeface(util.NormalFontFile) 193 | return context 194 | } 195 | -------------------------------------------------------------------------------- /goeasy/uimanager/layoutparam/layoutparam.go: -------------------------------------------------------------------------------- 1 | package layoutparam 2 | 3 | const ( 4 | FILL_PARENT = -1 5 | MATCH_PARENT = -1 6 | WRAP_CONTENT = -2 7 | ) 8 | 9 | type SizeEntry struct { 10 | Width int 11 | Height int 12 | } 13 | 14 | func (sizeEntry *SizeEntry) SetSize(width, height int) { 15 | sizeEntry.Width = width 16 | sizeEntry.Height = height 17 | } 18 | 19 | func NewLayoutParam(widthNew, heightNew int) *SizeEntry { 20 | return &SizeEntry{ 21 | Width: widthNew, 22 | Height: heightNew, 23 | } 24 | } 25 | 26 | func test() { 27 | test2 := NewLayoutParam(1, 2) 28 | test2.SetSize(2, 3) 29 | } 30 | -------------------------------------------------------------------------------- /goeasy/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | 6 | // "log" 7 | 8 | "GoEasy/goeasy/baseutil" 9 | 10 | "github.com/golang/freetype" 11 | "github.com/golang/freetype/truetype" 12 | "golang.org/x/image/font" 13 | "golang.org/x/image/math/fixed" 14 | ) 15 | 16 | const ( 17 | NormalFontFile = "Lantingcuhei.TTF" // 需要使用的字体文件 18 | ) 19 | 20 | func GetRealSize(oldSize float64, c *freetype.Context) int { 21 | return int(c.PointToFixed(oldSize) >> 6) 22 | } 23 | 24 | func GetTextLength(context *freetype.Context, s string, p fixed.Point26_6) (fixed.Point26_6, int, error) { 25 | if context.GetFont() == nil { 26 | return fixed.Point26_6{}, 0, errors.New("freetype: DrawText called with a nil font") 27 | } 28 | prev, hasPrev := truetype.Index(0), false 29 | maxOffsetY := 0 30 | for _, rune := range s { 31 | index := context.GetFont().Index(rune) 32 | // log.Println("index:", index) 33 | if hasPrev { 34 | kern := context.GetFont().Kern(context.GetScale(), prev, index) 35 | if context.GetHinting() != font.HintingNone { 36 | kern = (kern + 32) &^ 63 37 | } 38 | p.X += kern 39 | } 40 | advanceWidth, _, offset, err := context.Glyph(index, p) 41 | // log.Println("advanceWidth:%d", advanceWidth) 42 | if err != nil { 43 | return fixed.Point26_6{}, 0, err 44 | } 45 | // offset.Y = 0 46 | if baseutil.Abs(offset.Y) > maxOffsetY { 47 | maxOffsetY = baseutil.Abs(offset.Y) 48 | } 49 | p.X += advanceWidth 50 | //glyphRect := mask.Bounds().Add(offset) 51 | //dr := c.clip.Intersect(glyphRect) 52 | // if !dr.Empty() { 53 | // mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} 54 | // draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over) 55 | // } 56 | prev, hasPrev = index, true 57 | } 58 | // log.Println("maxOffsetY:", maxOffsetY) 59 | return p, maxOffsetY, nil 60 | } 61 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin linux windows 6 | 7 | // An app that draws a green triangle on a red background. 8 | // 9 | // Note: This demo is an early preview of Go 1.5. In order to build this 10 | // program as an Android APK using the gomobile tool. 11 | // 12 | // See http://godoc.org/golang.org/x/mobile/cmd/gomobile to install gomobile. 13 | // 14 | // Get the basic example and use gomobile to build or install it on your device. 15 | // 16 | // $ go get -d golang.org/x/mobile/example/basic 17 | // $ gomobile build golang.org/x/mobile/example/basic # will build an APK 18 | // 19 | // # plug your Android device to your computer or start an Android emulator. 20 | // # if you have adb installed on your machine, use gomobile install to 21 | // # build and deploy the APK to an Android target. 22 | // $ gomobile install golang.org/x/mobile/example/basic 23 | // 24 | // Switch to your device or emulator to start the Basic application from 25 | // the launcher. 26 | // You can also run the application on your desktop by running the command 27 | // below. (Note: It currently doesn't work on Windows.) 28 | // $ go install golang.org/x/mobile/example/basic && basic 29 | package main 30 | 31 | import ( 32 | "encoding/binary" 33 | "log" 34 | 35 | "GoEasy/goeasy/ui" 36 | 37 | "golang.org/x/mobile/app" 38 | "golang.org/x/mobile/event/lifecycle" 39 | "golang.org/x/mobile/event/paint" 40 | "golang.org/x/mobile/event/size" 41 | "golang.org/x/mobile/event/touch" 42 | "golang.org/x/mobile/exp/app/debug" 43 | "golang.org/x/mobile/exp/f32" 44 | "golang.org/x/mobile/exp/gl/glutil" 45 | "golang.org/x/mobile/gl" 46 | ) 47 | 48 | var ( 49 | images *glutil.Images 50 | fps *debug.FPS 51 | testTxt *ui.TextViewGo 52 | program gl.Program 53 | position gl.Attrib 54 | offset gl.Uniform 55 | color gl.Uniform 56 | buf gl.Buffer 57 | 58 | green float32 59 | touchX float32 60 | touchY float32 61 | 62 | startTouchX float32 63 | startTouchY float32 64 | ) 65 | 66 | func main() { 67 | app.Main(func(a app.App) { 68 | var glctx gl.Context 69 | var sz size.Event 70 | for e := range a.Events() { 71 | switch e := a.Filter(e).(type) { 72 | case lifecycle.Event: 73 | switch e.Crosses(lifecycle.StageVisible) { 74 | case lifecycle.CrossOn: 75 | glctx, _ = e.DrawContext.(gl.Context) 76 | onStart(glctx) 77 | a.Send(paint.Event{}) 78 | case lifecycle.CrossOff: 79 | onStop(glctx) 80 | glctx = nil 81 | } 82 | case size.Event: 83 | sz = e 84 | touchX = float32(sz.WidthPx / 2) 85 | touchY = float32(sz.HeightPx / 2) 86 | case paint.Event: 87 | if glctx == nil || e.External { 88 | // As we are actively painting as fast as 89 | // we can (usually 60 FPS), skip any paint 90 | // events sent by the system. 91 | continue 92 | } 93 | 94 | onPaint(glctx, sz) 95 | a.Publish() 96 | // Drive the animation by preparing to paint the next frame 97 | // after this one is shown. 98 | a.Send(paint.Event{}) 99 | case touch.Event: 100 | //touchX = e.X 101 | //touchY = e.Y 102 | onTouchEvent(e) 103 | } 104 | } 105 | }) 106 | } 107 | 108 | func onTouchEvent(touchEvent touch.Event) { 109 | switch touchEvent.Type { 110 | case touch.TypeBegin: 111 | startTouchX = touchEvent.X 112 | startTouchY = touchEvent.Y 113 | case touch.TypeMove: 114 | touchX = touchX + touchEvent.X - startTouchX 115 | touchY = touchY + touchEvent.Y - startTouchY 116 | startTouchX = touchEvent.X 117 | startTouchY = touchEvent.Y 118 | case touch.TypeEnd: 119 | } 120 | } 121 | 122 | func onStart(glctx gl.Context) { 123 | var err error 124 | program, err = glutil.CreateProgram(glctx, vertexShader, fragmentShader) 125 | if err != nil { 126 | log.Printf("error creating GL program: %v", err) 127 | return 128 | } 129 | 130 | buf = glctx.CreateBuffer() 131 | glctx.BindBuffer(gl.ARRAY_BUFFER, buf) 132 | glctx.BufferData(gl.ARRAY_BUFFER, triangleData, gl.STATIC_DRAW) 133 | position = glctx.GetAttribLocation(program, "position") 134 | color = glctx.GetUniformLocation(program, "color") 135 | offset = glctx.GetUniformLocation(program, "offset") 136 | 137 | images = glutil.NewImages(glctx) 138 | fps = debug.NewFPS(images) 139 | testTxt = ui.NewTextView() 140 | testTxt.SetSize(50, 20) 141 | testTxt.SetTextSize(6) 142 | testTxt.SetPosition(10, 20) 143 | testTxt.SetText("我是大神") 144 | } 145 | 146 | func onStop(glctx gl.Context) { 147 | glctx.DeleteProgram(program) 148 | glctx.DeleteBuffer(buf) 149 | fps.Release() 150 | images.Release() 151 | } 152 | 153 | func onPaint(glctx gl.Context, sz size.Event) { 154 | glctx.ClearColor(0.5, 0.2, 0.2, 1) 155 | glctx.Clear(gl.COLOR_BUFFER_BIT) 156 | 157 | glctx.UseProgram(program) 158 | 159 | green += 0.01 160 | if green > 1 { 161 | green = 0 162 | } 163 | glctx.Uniform4f(color, 0, green, 0, 1) 164 | 165 | glctx.Uniform2f(offset, touchX/float32(sz.WidthPx), touchY/float32(sz.HeightPx)) 166 | 167 | glctx.BindBuffer(gl.ARRAY_BUFFER, buf) 168 | glctx.EnableVertexAttribArray(position) 169 | glctx.VertexAttribPointer(position, coordsPerVertex, gl.FLOAT, false, 0, 0) 170 | glctx.DrawArrays(gl.TRIANGLES, 0, vertexCount) 171 | glctx.DisableVertexAttribArray(position) 172 | 173 | testTxt.Draw(images, sz) 174 | fps.Draw(sz) 175 | } 176 | 177 | var triangleData = f32.Bytes(binary.LittleEndian, 178 | 0.0, 0.4, 0.0, // top left 179 | 0.0, 0.0, 0.0, // bottom left 180 | 0.4, 0.0, 0.0, // bottom right 181 | ) 182 | 183 | const ( 184 | coordsPerVertex = 3 185 | vertexCount = 3 186 | ) 187 | 188 | const vertexShader = `#version 100 189 | uniform vec2 offset; 190 | 191 | attribute vec4 position; 192 | void main() { 193 | // offset comes in with x/y values between 0 and 1. 194 | // position bounds are -1 to 1. 195 | //右手坐标系原因,所以y值需要致反 196 | vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0); 197 | gl_Position = position + offset4; 198 | }` 199 | 200 | const fragmentShader = `#version 100 201 | precision mediump float; 202 | uniform vec4 color; 203 | void main() { 204 | gl_FragColor = color; 205 | }` 206 | -------------------------------------------------------------------------------- /main_x.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !darwin,!linux,!windows 6 | 7 | package main 8 | 9 | func main() { 10 | } 11 | --------------------------------------------------------------------------------