栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

android利用TextureView实现一套虚拟桌面框架

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

android利用TextureView实现一套虚拟桌面框架

工程目录

虚拟桌面的Demo工程架构如下:

其中app模块为主工程模块,加载TextureView并绘制虚拟桌面。
virtualapp1和virtualapp2模块为虚拟应用,通过加载主工程的Surface并绘制自己的虚拟界面。
virtual-lib为工程sdk,包含app模块调用的TextureView绘制的相关接口以及虚拟应用渲染组件。

效果

实现的简单Demo效果如下:

画面全部通过TextureView渲染而成,虚拟应用界面可响应触摸和点击事件。

virtual-lib介绍

VirtualService.java为每个虚拟应用需要声明的服务,以便主工程查找对应的入口。

public class VirtualService extends Service {

    private VirtualServiceBinder virtualServiceBinder;

    @Override
    public void onCreate() {
        super.onCreate();
        Looper renderLooper = Looper.getMainLooper();
        if (renderLooper == null) {
            int renderPriority = Process.THREAD_PRIORITY_DISPLAY;
            VirtualRenderThread widgetThread = new VirtualRenderThread(renderPriority);
            renderLooper = widgetThread.getLooper();
        }
        virtualServiceBinder = new VirtualServiceBinder(this, renderLooper);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return virtualServiceBinder;
    }

    static class VirtualServiceBinder extends IVirtualService.Stub {

        private Context context;
        private Looper widgetLooper;

        VirtualServiceBinder(Context context, Looper widgetLooper) {
            this.context = context;
            this.widgetLooper = widgetLooper;
        }

        @Override
        public IVirtualView getWidget(IVirtualClient virtualClient, Intent intent) throws RemoteException {
            try {
                VirtualHost virtualHost = new VirtualHost(context, intent, widgetLooper);
                try {
                    return virtualHost;
                } finally {
                    virtualHost.create(virtualClient);
                }
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        public void release(IVirtualView virtualView) throws RemoteException {
            if (virtualView != null) {
                IBinder binder = virtualView.asBinder();
                if (binder instanceof VirtualHost) {
                    ((VirtualHost) binder).destroy();
                }
            }
        }
    }
}

虚拟应用中需要AndroidManifest.xml中声明如下:

    
            

            
                
            
        

virtural_provider.xml声明如下:


    

虚拟应用通过VirtualComponent实现Surface的渲染

public abstract class VirtualComponent {

    private static final String TAG = VirtualComponent.class.getSimpleName();

    private final Object eventLock = new Object();
    private final Object surfaceLock = new Object();

    private volatile Surface surface;

    private Context context;
    private Looper renderLooper;
    private Rect virtualRect;
    private VirtualSurfaceLayout virtualSurfaceLayout;
    private Runnable renderRunnable;
    private volatile Handler renderHandler;

    private volatile boolean isCreated = false;
    private volatile boolean isAttachedToWindow = false;
    private volatile boolean isSurfaceCreated = false;

    private IVirtualClient virtualClient;

    @SuppressLint("ClickableViewAccessibility")
    final void init(Context context, Looper renderLooper) {
        this.context = context;
        this.renderLooper = renderLooper;
        virtualRect = new Rect();
        virtualSurfaceLayout = new VirtualSurfaceLayout(context);
        renderRunnable = new Runnable() {
            @Override
            public void run() {
                handleRender();
            }
        };
    }

    protected final Context getContext() {
        return context;
    }

    protected Looper getRenderLooper() {
        return renderLooper;
    }

    protected Handler getRenderHandler() {
        if (renderHandler == null) {
            synchronized (this) {
                if (renderHandler == null) {
                    renderHandler = new Handler(getRenderLooper());
                }
            }
        }
        return renderHandler;
    }


    @SuppressWarnings("unchecked")
    protected  T findViewById(int id) {
        return (T) virtualSurfaceLayout.findViewById(id);
    }

    protected void setContentView(int layoutId) {
        setContentView(View.inflate(context, layoutId, null));
    }

    protected void setContentView(View widgetView) {
        setContentView(widgetView, null);
    }

    protected void setContentView(View contentView, FrameLayout.LayoutParams layoutParams) {
        virtualSurfaceLayout.setContentView(contentView, layoutParams);
        ImageView imageView = new ImageView(getContext());
        imageView.setImageResource(R.drawable.ic_close);
        ViewGroup.LayoutParams itemParams = new ViewGroup.LayoutParams(200, 200);
        imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                view.performClick();
                if (virtualClient != null && motionEvent.getAction() == MotionEvent.ACTION_DOWN ) {

                    try {
                        if (surface != null) {
                            surface.release();
                            surface = null;
                        }
                        virtualClient.close();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                return true;
            }
        });
        virtualSurfaceLayout.addView(imageView, itemParams);
    }

    final void create() {
        synchronized (eventLock) {
            createInternal();
        }
    }

    private void createInternal() {
        if (!isCreated) {
            isCreated = true;
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    onCreated();
                    triggerRender();
                }
            });
        }
    }

    protected void onCreated() {
    }

    protected void onDestroyed() {
    }

    final void destroy() {
        synchronized (eventLock) {
            destroyInternal();
        }
    }

    private void destroyInternal() {
        if (isCreated) {
            isCreated = false;
            if (isSurfaceCreated()) {
                surfaceDestroyedInternal(surface);
            }
            if (isAttachedToWindow()) {
                detachedFromWindowInternal();
            }
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    onDestroyed();
                }
            });
        }
    }

    protected final boolean isCreated() {
        return isCreated;
    }


    final void attachedToWindow() {
        synchronized (eventLock) {
            attachedToWindowInternal();
        }
    }

    private void attachedToWindowInternal() {
        if (!isAttachedToWindow) {
            isAttachedToWindow = true;
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    virtualSurfaceLayout.dispatchAttachedToWindow();
                    triggerRender();
                }
            });
        }
    }

    final void detachedFromWindow() {
        synchronized (eventLock) {
            detachedFromWindowInternal();
        }
    }

    private void detachedFromWindowInternal() {
        if (isAttachedToWindow) {
            isAttachedToWindow = false;
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    virtualSurfaceLayout.dispatchDetachedFromWindow();
                }
            });
        }
    }

    protected final boolean isAttachedToWindow() {
        return isAttachedToWindow;
    }

    final void surfaceCreated(Surface surface) {
        synchronized (eventLock) {
            surfaceCreatedInternal(surface);
        }
    }

    private void surfaceCreatedInternal(final Surface surface) {
        if (!isSurfaceCreated) {
            isSurfaceCreated = true;
            synchronized (surfaceLock) {
                if (this.surface != null) {
                    this.surface.release();
                }
                this.surface = surface;
            }
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    triggerRender();
                }
            });
        }
    }

    final void surfaceDestroyed(Surface surface) {
        synchronized (eventLock) {
            surfaceDestroyedInternal(surface);
        }
    }

    private void surfaceDestroyedInternal(final Surface surface) {
        if (isSurfaceCreated) {
            isSurfaceCreated = false;
            synchronized (surfaceLock) {
                if (this.surface != null) {
                    this.surface.release();
                    this.surface = null;
                }
            }
            getRenderHandler().post(new Runnable() {
                @Override
                public void run() {
                    if (surface != null) {
                        surface.release();
                    }
                }
            });
        }
    }

    protected final boolean isSurfaceCreated() {
        return isSurfaceCreated;
    }

    final void surfaceChanged(Surface surface, int format, int width, int height) {
        synchronized (eventLock) {
            surfaceChangedInternal(surface, format, width, height);
        }
    }

    private void surfaceChangedInternal(final Surface surface, final int format, final int width, final int height) {
        synchronized (surfaceLock) {
            if (this.surface != null) {
                this.surface.release();
            }
            this.surface = surface;
        }
        getRenderHandler().post(new Runnable() {
            @Override
            public void run() {
                virtualRect.set(0, 0, width, height);
                ViewGroup.LayoutParams layoutParams = virtualSurfaceLayout.getLayoutParams();
                if (layoutParams == null) {
                    layoutParams = new ViewGroup.LayoutParams(width, height);
                    virtualSurfaceLayout.setLayoutParams(layoutParams);
                } else {
                    layoutParams.width = width;
                    layoutParams.height = height;
                }
                requestLayout();
                triggerRender();
            }
        });
    }


    final boolean handleTouchEvent(MotionEvent event) {
        synchronized (eventLock) {
            return dispatchTouchEventInternal(event);
        }
    }

    private boolean dispatchTouchEventInternal(MotionEvent event) {
        return dispatchTouchEvent(event);
    }

    protected boolean dispatchTouchEvent(final MotionEvent event) {
        getRenderHandler().post(new Runnable() {
            @Override
            public void run() {
                if (!virtualSurfaceLayout.dispatchTouchEvent(event)) {
                    onTouchEvent(event);
                }
            }
        });
        return true;
    }

    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }

    protected final void requestRender() {
        Handler renderHandler = getRenderHandler();
        if (!renderHandler.hasCallbacks(renderRunnable)) {
            renderHandler.post(renderRunnable);
        }
    }

    private void triggerRender() {
        if (isCreated()
                && isAttachedToWindow()
                && isSurfaceCreated()) {
            requestRender();
        }
    }

    private void handleRender() {
        doRender();
    }

    private void doRender() {
        synchronized (surfaceLock) {
            if (surface != null && surface.isValid()) {
                Canvas canvas = null;
                try {
                    requestLayout();
                    canvas = surface.lockCanvas(virtualRect);
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                    virtualSurfaceLayout.draw(canvas);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } finally {
                    if (canvas != null) {
                        try {
                            surface.unlockCanvasAndPost(canvas);
                        } catch (Exception ignore) {}
                    }
                }
            }
        }
    }

    private void requestLayout() {
        int widthSpec = View.MeasureSpec.makeMeasureSpec(virtualRect.width(), View.MeasureSpec.EXACTLY);
        int heightSpec = View.MeasureSpec.makeMeasureSpec(virtualRect.height(), View.MeasureSpec.EXACTLY);
        virtualSurfaceLayout.measure(widthSpec, heightSpec);
        virtualSurfaceLayout.layout(0, 0, virtualSurfaceLayout.getMeasuredWidth(), virtualSurfaceLayout.getMeasuredHeight());
    }

    public void setClient(IVirtualClient virtualClient) {
        this.virtualClient = virtualClient;
    }
}

VirtualAppUtils工具类来加载虚拟应用的相关参数:

public class VirtualAppUtils {

    private static final String KEY_WIDGET_PROVIDER = "com.example.virtualproject.PROVIDER";

    private static final String TAG_WIDGET = "VirtualView";
    private static final String ATTR_WIDGET_CLASS = "widgetClass";
    private static final String ATTR_WIDGET_ICON = "widgetIcon";
    private static final String ATTR_WIDGET_LAYOUT = "widgetLayout";

    public static List getVirtualAppInfos(Context context) {
        List virtualProviders = new ArrayList<>();
        PackageManager packageManager = context.getPackageManager();
        List resolveInfos = packageManager.queryIntentServices(new Intent(VirtualViewHelper.WIDGET_SERVICE_ACTION), PackageManager.GET_META_DATA);
        for (ResolveInfo info : resolveInfos) {
            info.getIconResource();
            if (info.serviceInfo != null) {
                try (XmlResourceParser parser = info.serviceInfo.loadXmlMetaData(packageManager, KEY_WIDGET_PROVIDER)) {
                    if (parser != null) {
                        try {
                            Resources widgetResources = packageManager.getResourcesForApplication(info.serviceInfo.applicationInfo);
                            virtualProviders.addAll(parseWidgetProvider(info.serviceInfo.applicationInfo, packageManager, widgetResources, parser));
                        } catch (IOException | XmlPullParserException | PackageManager.NameNotFoundException ignore) {}
                    }
                }
            }
        }
        return virtualProviders;
    }

    private static List parseWidgetProvider(ApplicationInfo applicationInfo, PackageManager packageManager, Resources widgetResources, XmlResourceParser parser)
            throws IOException, XmlPullParserException {
        List virtualProviders = new ArrayList<>();
        while(parser.next() != XmlResourceParser.END_DOCUMENT) {
            if(parser.getEventType() == XmlResourceParser.START_TAG && TAG_WIDGET.equals(parser.getName())) {
                VirtualProvider virtualProvider = new VirtualProvider(applicationInfo.packageName, widgetResources);
                virtualProvider.setIconId(applicationInfo.icon);
                virtualProvider.setIconDrawable(applicationInfo.loadIcon(packageManager));
                virtualProvider.setLabel(applicationInfo.loadLabel(packageManager).toString());
                for (int i = 0; i < parser.getAttributeCount(); i++) {
                    String name = parser.getAttributeName(i);
                    if (ATTR_WIDGET_CLASS.equals(name)) {
                        virtualProvider.setClassName(parser.getAttributeValue(i));
                    } else if (ATTR_WIDGET_ICON.equals(name)) {
                        virtualProvider.setIconId(parser.getAttributeResourceValue(i, applicationInfo.icon));
                    } else if (ATTR_WIDGET_LAYOUT.equals(name)) {
                        virtualProvider.setLayout(parser.getAttributeIntValue(i, 0));
                    }
                }
                if (virtualProvider.getLabel() == null) {
                    if (applicationInfo.labelRes != 0) {
                        virtualProvider.setLabel(widgetResources.getString(applicationInfo.labelRes));
                    } else {
                        virtualProvider.setLabel(applicationInfo.packageName);
                    }
                }
                if (virtualProvider.getIconId() == 0) {
                    virtualProvider.setIconId(applicationInfo.icon);
                }
                virtualProviders.add(virtualProvider);
            }
        }
        return virtualProviders;
    }
}

由于TexttureView不同于在Activity中添加View的流程,需要通过反射回调连接过程:

@SuppressLint("ViewConstructor")
public class VirtualSurfaceLayout extends FrameLayout {

    private boolean isAttachedToWindow;

    private Method onAttachedToWindowMethod;
    private Method onDetachedFromWindowMethod;

    public VirtualSurfaceLayout(Context context) {
        super(context);
        new FrameLayout(context).addView(this);
    }

    public final void setContentView(View contentView, ViewGroup.LayoutParams layoutParams) {
        removeAllViews();
        if (contentView != null) {
            if (layoutParams == null) {
                layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            }
            addView(contentView, layoutParams);
        }
    }

    final void dispatchAttachedToWindow() {
        if (!isAttachedToWindow) {
            isAttachedToWindow = true;
            dispatchAttachedToWindow(this);

        }
    }

    final void dispatchDetachedFromWindow() {
        if (isAttachedToWindow) {
            isAttachedToWindow = false;
            dispatchDetachedFromWindow(this);
        }
    }

    @Override
    public boolean isAttachedToWindow() {
        return isAttachedToWindow;
    }


    private void dispatchAttachedToWindow(View view) {
        if (view != null) {
            if (onAttachedToWindowMethod == null) {
                try {
                    onAttachedToWindowMethod = View.class.getDeclaredMethod("onAttachedToWindow");
                    onAttachedToWindowMethod.setAccessible(true);
                } catch (NoSuchMethodException e) {
                    return;
                }
            }
            try {
                onAttachedToWindowMethod.invoke(view);
            } catch (IllegalAccessException | InvocationTargetException ignore) {}
            if (view instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) view;
                for (int i = 0; i < viewGroup.getChildCount(); i++) {
                    dispatchAttachedToWindow(viewGroup.getChildAt(i));
                }
            }
        }
    }

    private void dispatchDetachedFromWindow(View view) {
        if (view != null) {
            if (onDetachedFromWindowMethod == null) {
                try {
                    onDetachedFromWindowMethod = View.class.getDeclaredMethod("onDetachedFromWindow");
                    onDetachedFromWindowMethod.setAccessible(true);
                } catch (NoSuchMethodException e) {
                    return;
                }
            }
            try {
                onDetachedFromWindowMethod.invoke(view);
            } catch (IllegalAccessException | InvocationTargetException ignore) {}
            if (view instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) view;
                for (int i = 0; i < viewGroup.getChildCount(); i++) {
                    dispatchDetachedFromWindow(viewGroup.getChildAt(i));
                }
            }
        }
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        if (isAttachedToWindow()) {
            dispatchAttachedToWindow(child);
        }
        child.setSelected(isSelected());
    }

    @Override
    public void onViewRemoved(View child) {
        super.onViewRemoved(child);
        if (isAttachedToWindow()) {
            dispatchDetachedFromWindow(child);
        }
    }


}

应用

可实现多个TextrueView绑定多个虚拟应用显示,组建一个多组件/卡片显示的主界面。也可通过将Surface编码然后跨设备传输,在另一设备界面渲染,实现如HiCar、CarPlay的虚拟桌面效果。
未完待续。。。
待上传源码

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/932571.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号