using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using Android.App; using Android.Content; using Android.Graphics; using Android.OS; using Android.Runtime; using Android.Views; using Android.Views.InputMethods; using Android.Widget; using CPF.Drawing; using CPF.Input; using CPF.OpenGL; using CPF.Platform; using Javax.Microedition.Khronos.Egl; using GLES20 = global::Android.Opengl.GLES20; namespace CPF.Android { internal class AndroidView : SurfaceView, ISurfaceView, ISurfaceHolderCallback { public AndroidView(Context activity, UIElement content, CpfView cpfView) : base(activity) { if (CPF.Platform.Application.GetDrawingFactory().UseGPU) { eglContext = new EglContext(this); Holder.AddCallback(this); //SetLayerType(LayerType.Hardware, null); } CpfView = cpfView; this.Holder.SetFormat(Format.Rgba8888); //SetOnTouchListener(this); //SetOnKeyListener(this); //this.activity = activity as Activity; //if (content != null) //{ // root = new InnerView(this); // root.Background = CPF.Drawing.Color.White; // root.Children.Add(content); // root.Visibility = CPF.Visibility.Visible; // root.CanActivate = true; // root.LayoutUpdated += Root_LayoutUpdated; // scale = root.LayoutScaling; //} //this.FocusableInTouchMode = true; //softKeyboardListner = new SoftKeyboardListner(this); //ViewTreeObserver.AddOnGlobalLayoutListener(softKeyboardListner); generalView = new GeneralView(this, content); cpfView.AddView(this); generalView.Create(); } EglContext eglContext; GeneralView generalView; public CpfView CpfView { get; } public GeneralView GeneralView { get { return generalView; } } //internal Activity activity; //CPF.Controls.View root; //PixelSize oldSize; public CPF.Controls.View Root { get { return generalView.Root; } } protected override int[] OnCreateDrawableState(int extraSpace) { //Invalidate(); generalView.OnCreateDrawableState(extraSpace); return base.OnCreateDrawableState(extraSpace); } //bool _invalidateQueued; public override void Invalidate() { //if (_invalidateQueued) //{ // return; //} //_invalidateQueued = true; //Threading.Dispatcher.MainThread.BeginInvoke(() => //{ // _invalidateQueued = false; // if (CpfView.Handle != default) // { // OnPaint(); // } //}); generalView.Invalidate(); } public override void Invalidate(global::Android.Graphics.Rect dirty) { Invalidate(); } public override void Invalidate(int l, int t, int r, int b) { Invalidate(); } //float scale; protected override void OnLayout(bool changed, int left, int top, int right, int bottom) { //base.OnLayout(changed, left, top, right, bottom); //var size = new PixelSize(right, bottom); //var sc = LayoutScaling; //if (LayoutScaling != scale) //{ // scale = sc; // ScalingChanged(); //} //if (size != oldSize) //{ // oldSize = size; // Resized(new Size(size.Width / LayoutScaling, size.Height / LayoutScaling)); //} generalView.OnLayout(changed, left, top, right, bottom); } PixelSize pixelSize; public virtual void OnPaint() { //System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); //stopwatch.Start(); var root = generalView.Root; if (eglContext != null) { var _window = ANativeWindow_fromSurface(JNIEnv.Handle, Holder.Surface.Handle); if (_window == IntPtr.Zero) return;//获取window操作可以解决下拉框图像错位问题 ANativeWindow_release(_window); root.LayoutManager.ExecuteLayoutPass(); if (eglContext.EglSurface != null) { eglContext.MakeCurrent(); //var ir = generalView.invalidateRect * RenderScaling; //GLES20.GlViewport(ir.X,ir.Y,ir.Width,ir.Height); eglContext.GetFramebufferInfo(out var fb, out var sam, out var ste); using (DrawingContext dc = DrawingContext.FromRenderTarget(new OpenGlRenderTarget(eglContext, pixelSize.Width, pixelSize.Height, fb, sam, ste))) { if (root.LayoutManager.VisibleUIElements != null) { root.RenderView(dc, new Drawing.Rect(0, 0, pixelSize.Width, pixelSize.Height)); } } eglContext.SwapBuffers(); } } else { var surface = this.Holder.Surface; var _window = ANativeWindow_fromSurface(JNIEnv.Handle, surface.Handle); if (_window == IntPtr.Zero) return; //throw new Exception("Unable to obtain ANativeWindow"); ANativeWindow_Buffer buffer; var rc = new ARect() { right = ANativeWindow_getWidth(_window), bottom = ANativeWindow_getHeight(_window) }; var size = new PixelSize(rc.right, rc.bottom); if (size.Width != Width || size.Height != Height) {//有时候渲染尺寸和控件尺寸不一样 //surface.SetSize(size.Width, size.Height); //Holder.SetSizeFromLayout(); Holder.SetFixedSize(size.Width, size.Height); } rc = new ARect() { right = ANativeWindow_getWidth(_window), bottom = ANativeWindow_getHeight(_window) }; size = new PixelSize(rc.right, rc.bottom); ANativeWindow_lock(_window, out buffer, ref rc); root.LayoutManager.ExecuteLayoutPass(); var Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 ? CPF.Drawing.PixelFormat.Rgb565 : CPF.Drawing.PixelFormat.Rgba; var RowBytes = buffer.stride * (Format == CPF.Drawing.PixelFormat.Rgb565 ? 2 : 4); var Address = buffer.bits; using (Drawing.Bitmap bmp = new Drawing.Bitmap(size.Width, size.Height, RowBytes, Format, Address)) { using (DrawingContext dc = DrawingContext.FromBitmap(bmp)) { if (root.LayoutManager.VisibleUIElements != null) { //generalView.invalidateRect * RenderScaling root.RenderView(dc, new Drawing.Rect(0,0,size.Width,size.Height)); } } } ANativeWindow_unlockAndPost(_window); ANativeWindow_release(_window); } //System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds); } public override IInputConnection OnCreateInputConnection(EditorInfo outAttrs) { return generalView.OnCreateInputConnection(outAttrs); } public bool OnKey(View v, [GeneratedEnum] Keycode keyCode, KeyEvent e) { generalView.OnKey(v, keyCode, e); return false; } public bool OnTouch(View v, MotionEvent e) { return generalView.OnTouch(v, e); } //public override bool DispatchKeyEvent(KeyEvent e) //{ // //var m = new WindowManagerLayoutParams(100, 100, 100, 100, WindowManagerTypes.Phone | WindowManagerTypes.ApplicationOverlay, WindowManagerFlags.NotFocusable, global::Android.Graphics.Format.Rgbx8888); // ////m.Gravity = GravityFlags.Left | GravityFlags.Top; // //activity.WindowManager.AddView(new TestView(activity) { Background = new global::Android.Graphics.Drawables.ColorDrawable(global::Android.Graphics.Color.Argb(100, 255, 0, 0)) }, m); //} protected override void DispatchSetActivated(bool activated) { base.DispatchSetActivated(activated); //if (activated) //{ // (this as IViewImpl).Activated(); //} //else //{ // Deactivated(); //} generalView.DispatchSetActivated(activated); } [DllImport("android")] internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle); [DllImport("android")] internal static extern int ANativeWindow_getWidth(IntPtr window); [DllImport("android")] internal static extern int ANativeWindow_getHeight(IntPtr window); [DllImport("android")] internal static extern void ANativeWindow_release(IntPtr window); [DllImport("android")] internal static extern void ANativeWindow_unlockAndPost(IntPtr window); [DllImport("android")] internal static extern int ANativeWindow_lock(IntPtr window, out ANativeWindow_Buffer outBuffer, ref ARect inOutDirtyBounds); public enum AndroidPixelFormat { WINDOW_FORMAT_RGBA_8888 = 1, WINDOW_FORMAT_RGBX_8888 = 2, WINDOW_FORMAT_RGB_565 = 4, } internal struct ARect { public int left; public int top; public int right; public int bottom; } internal struct ANativeWindow_Buffer { // The number of pixels that are show horizontally. public int width; // The number of pixels that are shown vertically. public int height; // The number of *pixels* that a line in the buffer takes in // memory. This may be >= width. public int stride; // The format of the buffer. One of WINDOW_FORMAT_* public AndroidPixelFormat format; // The actual bits. public IntPtr bits; // Do not touch. uint reserved1; uint reserved2; uint reserved3; uint reserved4; uint reserved5; uint reserved6; } public Screen Screen { get { return generalView.Screen; } } public float RenderScaling { get { //return global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity; return generalView.RenderScaling; } } public float LayoutScaling => RenderScaling; public Action ScalingChanged { get => generalView.ScalingChanged; set => generalView.ScalingChanged = value; } public Action Resized { get => generalView.Resized; set => generalView.Resized = value; } Action IViewImpl.PositionChanged { get => generalView.PositionChanged; set => generalView.PositionChanged = value; } Action IViewImpl.Activated { get => generalView.Activated; set => generalView.Activated = value; } public Action Deactivated { get => generalView.Deactivated; set => generalView.Deactivated = value; } bool IViewImpl.CanActivate { get => generalView.CanActivate; set => generalView.CanActivate = value; } PixelPoint IViewImpl.Position { get { return generalView.Position; } set { generalView.Position = value; } } void IViewImpl.Activate() { //throw new NotImplementedException(); //RequestFocus(); generalView.Activate(); } void IViewImpl.Capture() { //RequestPointerCapture(); //throw new NotImplementedException(); generalView.Capture(); } void IViewImpl.Invalidate(in Drawing.Rect rect) { generalView.Invalidate(in rect); } Drawing.Point IViewImpl.PointToClient(Drawing.Point point) { return generalView.PointToClient(point); } Drawing.Point IViewImpl.PointToScreen(Drawing.Point point) { return generalView.PointToScreen(point); } void IViewImpl.ReleaseCapture() { generalView.ReleaseCapture(); } void IViewImpl.SetCursor(Cursor cursor) { //throw new NotImplementedException(); generalView.SetCursor(cursor); } //internal bool IMEEnable = true; void IViewImpl.SetIMEEnable(bool enable) { //IMEEnable = enable; generalView.SetIMEEnable(enable); } //bool showInput; //internal static IEditor editor; public void ShowKeyboard(bool show, IEditor editor) { generalView.ShowKeyboard(show, editor); } void IViewImpl.SetIMEPosition(Drawing.Point point) { } void IViewImpl.SetRoot(Controls.View view) { generalView.SetRoot(view); } void IViewImpl.SetVisible(bool visible) { generalView.SetVisible(visible); } protected override void Dispose(bool disposing) { eglContext?.Dispose(); eglContext = null; base.Dispose(disposing); } public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height) { pixelSize = new PixelSize(width, height); eglContext.OnDestroySurface(); eglContext.OnCreateSurface(); //GLES20.GlViewport(0, 0, width, height); } public void SurfaceCreated(ISurfaceHolder holder) { eglContext?.OnCreateSurface(); } public void SurfaceDestroyed(ISurfaceHolder holder) { eglContext?.OnDestroySurface(); } //public bool OnCapturedPointer(View view, MotionEvent e) //{ // System.Diagnostics.Debug.WriteLine(view + "--" + e.Action); // return false; //} } class InnerView : CPF.Controls.View { ISurfaceView cpfView; public InnerView(ISurfaceView cpfView) : base(cpfView) { this.cpfView = cpfView; } protected override IViewImpl CreateView() { return cpfView; } } //class TestView : View //{ // public TestView(global::Android.Content.Context context) : base(context) // { // Focusable = true; // FocusableInTouchMode = true; // } //} }