【WM】Phone Canvas重复创建视图的原因

本文分析了在H.324协议栈线程内调用PhoneCanvas函数导致cprog.exe创建多个ProgressView的问题。解释了由于线程上下文不同而造成的视图混乱现象,并给出了确保视图在同一线程中创建的解决方案。

记得08年刚做可视电话模块的时候,我们被Phone Canvas函数不返回RPC_E_WRONG_THREAD现象所迷惑,在H.324协议栈线程内调用了Phone Canvas函数导致cprog.exe创建了两个Progress View。结果两个Progress View交替显示,状态那叫一个混乱哟!

解决这个问题的方法,笔者已在《【WM】谈Phone Canvas函数为何不会返回错误值RPC_E_WRONG_THREAD》一文中给出,即:Phone Canvas函数需要在cprog.exe的UI线程上下文中调用,至于造成这个问题的原因将在本文中分析。

Phone Canvas的Dialer、Progress和SmartDialer视图都派生自同一个基类CProgressCommon,这个基类下面有个EnsureViewExists()方法:

在EnsureViewExists()方法内对视图的线程上下文进行了验证,如果视图不在当前线程上下文创建的,则重新创建视图。

因此,上述故障就很容易理解了:cprog.exe启动时UI线程创建了一个Progress视图,然后我们在H.324协议栈线程上下文间接调用了EnsureViewExists()方法又创建了一个Progress视图。

package com.tencent.acgui; import android.app.*; import android.content.*; import android.content.res.*; import android.graphics.*; import android.os.*; import android.util.*; import android.view.*; public class FloatService extends Service { public static FloatService Instance; public static WindowManager manager = null; private MySurfaceView surfaceView = null; public static boolean mSurfaceViewIo = false; private DisplayMetrics dm = new DisplayMetrics(); private WindowManager.LayoutParams wmParams; public static void ShowFloat(Context context) { if (Instance == null) { Intent intent = new Intent(context, FloatService.class); context.startService(intent); } } @Override public void onCreate() { super.onCreate(); Instance = this; initData(); SetmSurfaceView(); } @Override public IBinder onBind(Intent intent) { return null; } private void initData() { if (manager == null) { manager = (WindowManager) Instance.getSystemService(WINDOW_SERVICE); } } private void SetmSurfaceView() { surfaceView = new MySurfaceView(Instance); surfaceView.setZOrderOnTop(true); manager.getDefaultDisplay().getRealMetrics(dm); surfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT); wmParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { wmParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } } else { wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; } wmParams.format = PixelFormat.RGBA_8888; wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN; wmParams.gravity = Gravity.LEFT | Gravity.TOP; wmParams.x = 0; wmParams.y = 0; wmParams.width = dm.widthPixels; wmParams.height = dm.heightPixels; wmParams.alpha = 0.5f; if (manager != null) { mSurfaceViewIo = true; manager.addView(surfaceView, wmParams); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); manager.getDefaultDisplay().getRealMetrics(dm); if (wmParams != null && surfaceView != null) { wmParams.width = dm.widthPixels; wmParams.height = dm.heightPixels; manager.updateViewLayout(surfaceView, wmParams); } } } package com.tencent.acgui; import android.annotation.*; import android.content.*; import android.graphics.*; import android.os.*; import android.view.*; import android.view.inputmethod.*; public class TouchView extends View { private View mtouch; private InputMethodManager manager; private WindowManager.LayoutParams mtouchParams; public TouchView(Context context) { super(context); mtouch = this; manager = (InputMethodManager) mtouch.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); } private WindowManager.LayoutParams getAttributes(boolean isWindow) { WindowManager.LayoutParams params = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { params.type = WindowManager.LayoutParams.TYPE_PHONE; } params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; if (isWindow) { params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; } params.format = PixelFormat.RGBA_8888; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } params.gravity = Gravity.LEFT | Gravity.TOP; params.x = params.y = 0; params.width = params.height = isWindow ? WindowManager.LayoutParams.WRAP_CONTENT : WindowManager.LayoutParams.WRAP_CONTENT; return params; } public void initView() { mtouchParams = getAttributes(false); FloatService.manager.addView(mtouch, mtouchParams); updateTouchWinSize(); } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { JNIInterface.MotionEventClick(event.getAction(), event.getRawX(), event.getRawY()); return false; } private void updateTouchWinSize() { final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { float[] rect = JNIInterface.GetImGuiwinsize(); mtouchParams.x = (int) rect[0]; mtouchParams.y = (int) rect[1]; mtouchParams.width = (int) rect[2]; mtouchParams.height = (int) rect[3]; FloatService.manager.updateViewLayout(mtouch, mtouchParams); handler.postDelayed(this, 20); } }, 20); } } package com.tencent.acgui; import android.annotation.*; import android.content.*; import android.graphics.*; import android.util.*; import android.view.*; public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { private TouchView mtouch; private boolean jniLoaded = false; public MySurfaceView(Context context) { this(context, null); } private MySurfaceView(Context context, AttributeSet attrs) { this(context, attrs, 0); } private MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mtouch = new TouchView(context); this.setZOrderOnTop(true); this.getHolder().setFormat(PixelFormat.RGBA_8888); this.getHolder().addCallback(this); mtouch.initView(); } private void checkJNILoaded() { try { JNIInterface.verifySignature(getContext()); jniLoaded = true; } catch (UnsatisfiedLinkError e) { jniLoaded = false; try { System.loadLibrary("AcGui"); jniLoaded = true; } catch (Exception ex) { ex.printStackTrace(); } } } @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { checkJNILoaded(); if (!jniLoaded) { JNIInterface.SurfaceCreate(holder.getSurface(), this.getWidth(), this.getHeight()); return; } try { byte[] fontData = JNIInterface.loadFontFromAssets(getContext(), "TextFont.ttf"); if (fontData != null && fontData.length > 0) { JNIInterface.setCustomFont(fontData, fontData.length); } } catch (UnsatisfiedLinkError e) { } catch (Exception e) { } JNIInterface.SurfaceCreate(holder.getSurface(), this.getWidth(), this.getHeight()); } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int Width, int Height) { if (jniLoaded) { JNIInterface.SurfaceChange(Width, Height); } } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (jniLoaded) { JNIInterface.SurfaceDestroyed(); } } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { if (jniLoaded) { JNIInterface.MotionEventClick(event.getAction(), event.getRawX(), event.getRawY()); } return false; } }
最新发布
12-12
import tkinter from tkinter import ttk from tkinter import messagebox from tkinter import * import csv #定义公共变量 global k,maxrecno,xm,xb,nl,rw,sx,yy,item_id,cr1,image1,image2,image3,label11,jpgname def add_student(): #录入数据 global maxrecno,xm,xb,nl,rw,sx,yy def search_student(): #查询 global maxrecno,xm,xb,nl,rw,sx,yy def delete_student(): #删除 global maxrecno,xm,xb,nl,rw,sx,yy def update_student(): #修改 global maxrecno,xm,xb,nl,rw,sx,yy def brows_student(): #浏览 #清空tree clear1() #往tree中填入数据,以显示 with open('students.csv', 'r', newline='') as file: reader = csv.reader(file) i=1 for row in reader: tree.insert('',i,text=i,values=(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9],row[10],row[11])) i+=1 def view_student():#定义视图 global maxrecno,xm,xb,nl,rw,sx,yy def clear(): #把10个接收窗口清空 entry1.delete(0,len(entry1.get())) entry2.delete(0,len(entry2.get())) entry3.delete(0,len(entry3.get())) entry4.delete(0,len(entry4.get())) entry5.delete(0,len(entry5.get())) entry6.delete(0,len(entry6.get())) entry7.delete(0,len(entry7.get())) entry8.delete(0,len(entry8.get())) entry9.delete(0,len(entry9.get())) entry10.delete(0,len(entry10.get())) def clear1(): #清空tree.insert() x1 = tree.get_children() for item in x1: tree.delete(item) k=1 def Login():#登录函数 global k Name = enterName.get() Pwd = enterPwd.get() if Name=='admin' and Pwd=='admin': window.wm_deiconify() #显示window主窗口 win2.withdraw() #隐藏win2登录子窗口 if k==3: messagebox.showerror('温馨提示','用户名或者密码输错3次了,你是在尝试? 退出!') Cancel() k+=1 def Cancel():#退出函数 win2.quit() window.quit() #退出 window.destroy() #撒销进程 window = Tk() window.title("学生管理系统") window.geometry('1200x520+200+10') #定义命令按键 button0 = ttk.Button(window, text="清空录入区") button1 = ttk.Button(window, text="录入") button2 = ttk.Button(window, text="查询") button3 = ttk.Button(window, text="删除") button4 = ttk.Button(window, text="修改") button5 = ttk.Button(window, text="浏览数据") button6 = ttk.Button(window, text="统计视图") button0.place(x=300, y=450) button1.place(x=390, y=450) button2.place(x=480, y=450) button3.place(x=570, y=450) button4.place(x=660, y=450) button5.place(x=750, y=450) button6.place(x=840, y=450) #利用ttk的特性设置动感按键 style = ttk.Style(window) style.map('TButton',foreground=[('pressed', 'blue'),('active', 'red')]) #定义标签 L1 = Label(window, text='姓名:') L2 = Label(window, text='性别:') L3 = Label(window, text='年龄:') L4 = Label(window, text='籍贯:') L5 = Label(window, text='住址:') L6 = Label(window, text='电话:') L7 = Label(window, text='语文:') L8 = Label(window, text='数学:') L9 = Label(window, text='英语:') L10 = Label(window, text='总分:') L11 = Label(window, text=' 数 据 录 入 区 ',fg='red') L12 = Label(window, text=' 数 据 浏 览 区 ',fg='red') L13 = Label(window, text='相片') L1.place(x=10,y=50) L2.place(x=10,y=80) L3.place(x=10,y=110) L4.place(x=10,y=140) L5.place(x=10,y=170) L6.place(x=10,y=200) L7.place(x=10,y=230) L8.place(x=10,y=260) L9.place(x=10,y=290) L10.place(x=10,y=320) L11.place(x=40,y=15) L12.place(x=600,y=15) L13.place(x=85,y=490) #定义单行文本框 entry1 = ttk.Entry(window) entry2 = ttk.Entry(window) entry3 = ttk.Entry(window) entry4 = ttk.Entry(window) entry5 = ttk.Entry(window) entry6 = ttk.Entry(window) entry7 = ttk.Entry(window) entry8 = ttk.Entry(window) entry9 = ttk.Entry(window) entry10 = ttk.Entry(window) entry1.place(x=55,y=50) entry2.place(x=55,y=80) entry3.place(x=55,y=110) entry4.place(x=55,y=140) entry5.place(x=55,y=170) entry6.place(x=55,y=200) entry7.place(x=55,y=230) entry8.place(x=55,y=260) entry9.place(x=55,y=290) entry10.place(x=55,y=320) #创建tree表格,显示记录 tree =ttk.Treeview(window,show="headings",selectmode="browse",height=18) #只显示表头show="headings",height为tree窗口高度 #为tree设置垂直滚动条 yscrollbar = Scrollbar(window)#定义一个垂直滚动条 yscrollbar.pack(side=RIGHT,fill=Y)#放置在右边,亲充满 yscrollbar.config(command=tree.yview)#设置游动条的command回调函数yview() tree.configure(yscrollcommand=yscrollbar.set)#垂直游动条绑定多行文本框 #给tree指定位置(不能前于垂直滚动条,注意先后次序) tree.pack(padx=(230,10),pady=(50,30)) #指定位置x方向左230、右20,上50、下30,宽度为900-230-10=660 #定义一个tre表格框架 tree['columns']=('序号','姓名','性别','年龄','籍贯','住址','电话','语文','数学','英语','总分','相片名称') #设置列,总宽为900-230-10=660 tree.column('序号',width=50,anchor="center") tree.column('姓名',width=50,anchor="center") tree.column('性别',width=50,anchor="center") tree.column('年龄',width=50,anchor="center") tree.column('籍贯',width=50,anchor="center") tree.column('住址',width=200,anchor="center") tree.column('电话',width=100,anchor="center") tree.column('语文',width=70,anchor="center") tree.column('数学',width=70,anchor="center") tree.column('英语',width=70,anchor="center") tree.column('总分',width=70,anchor="center") tree.column('相片名称',width=100,anchor="center") #设置表头 tree.heading('序号',text='序号') tree.heading('姓名',text='姓名') tree.heading('性别',text='性别') tree.heading('年龄',text='年龄') tree.heading('籍贯',text='籍贯') tree.heading('住址',text='住址') tree.heading('电话',text='电话') tree.heading('语文',text='语文') tree.heading('数学',text='数学') tree.heading('英语',text='英语') tree.heading('总分',text='总分') tree.heading('相片名称',text='相片名称') #定义与tree关联的鼠标事件,通过鼠标在tree中选择,把选中的记录转入左边7个单行文本框内显示 def mouseselect(event): global item_id,cr1,jpgname # 获取选中项的ID item_id = event.widget.selection()[0] # 获取该项的所有列的值 cr1 = event.widget.item(item_id, "values") #把7个接收窗口清空 clear() #通过鼠标选择,把记录内容放进单行文本框 entry1.insert(0,cr1[1]) entry2.insert(0,cr1[2]) entry3.insert(0,cr1[3]) entry4.insert(0,cr1[4]) entry5.insert(0,cr1[5]) entry6.insert(0,cr1[6]) entry7.insert(0,cr1[7]) entry8.insert(0,cr1[8]) entry9.insert(0,cr1[9]) entry10.insert(0,cr1[10]) jpgname=cr1[11] showjpg() tree.bind('<<TreeviewSelect>>',mouseselect) #tree绑定鼠标单击 #定义登录窗口 win2=tkinter.Toplevel(window) win2.title('登录验证') win2.config(background="LavenderBlush") win2.geometry('270x170+800+200') #10左距离 10上距离 # 创建登录账户的标签 labname = tkinter.Label(win2,text=' 用户名 ',justify=tkinter.RIGHT,bg='pink',width=10) labPwd = tkinter.Label(win2,text=' 密 码',justify=tkinter.RIGHT,bg='pink',width=10) labname.place(x=40,y=20) labPwd.place(x=40,y=50) # label 标签(用户名和密码的变量) varName = StringVar() # 文字变量储存器器 varName.set('guest') varPwd = StringVar() varPwd.set('123456') # 创建登录文本框,同时设置关联变量 enterName = tkinter.Entry(win2,width=12,textvariable=varName) enterPwd = tkinter.Entry(win2,show='*',width=12,textvariable=varPwd) enterName.place(x=130,y=20) enterPwd.place(x=130,y=50) # 创建登录按钮组件,并且设置按钮事件的处理函数 bOK = tkinter.Button(win2,text = '登录',bg='lightskyblue',width=7,command =Login) bCancel =tkinter.Button(win2,text='退出',bg='lightgray',width=7,command=Cancel) bOK.place(x=60 , y=100) bCancel.place(x=150,y=100) #首次进入系统,先显示数据 brows_student() window.withdraw() #隐藏window主窗口 win2.wm_deiconify() #显示win2登录子窗口 window.mainloop() 增加创新
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值