自定义view中错误:No resource identifier found for attribute X in package X

本文详细解释了在Android开发中遇到XML传递参数时出现No resource identifier found for attribute X in package X错误的原因及解决方法。通过调整命名空间,确保XML中的属性引用与AndroidManifest中的包名保持一致,从而成功解决问题。

今天在写一个自定义view时,加入了通过xml传递参数,但是xml出现了No resource identifier found for attribute X in package X,我看了下教程感觉好像基本上代码差不多,后来查了google,发现这个声明的命名空间需要和androidManifest中的package对应,即命名空间为

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tino.fingerscanning"
    android:versionCode="1"
    android:versionName="1.0" >

中的xmlns:android中后面的命名空间去掉android加上package中的路径,比如我的xmlns应该是

xmlns:myswitch="http://schemas.android.com/apk/res/  com.tino.fingerscanning"


1.修改代码,使代码正常运行,不丢失功能 2.修改判断逻辑,不通过当前页面是否访问过来决定是否返回上一页面,而是判断当前页面可点击的元素是否都全部点击,全部点击返回上一界面 from appium import webdriver from appium.options.android import UiAutomator2Options from selenium.common.exceptions import ElementClickInterceptedException, StaleElementReferenceException, \ TimeoutException from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import hashlib # ==================== 目标应用配置 ==================== TARGET_PACKAGE = "com.brother.ptouch.iprintandlabel" # ==================== 配置 Appium Options ==================== options = UiAutomator2Options() options.set_capability("platformName", "Android") options.set_capability("deviceName", "emulator-5554") # 确保设备在线 options.set_capability("automationName", "UiAutomator2") options.set_capability("noReset", True) # 不清除数据(保留登录状态等) options.set_capability("fullReset", False) options.set_capability("autoGrantPermissions", True) options.set_capability("newCommandTimeout", 600) # 启动 App(假设已安装) driver = webdriver.Remote(command_executor='http://localhost:4723', options=options) # 存储已处理元素和访问过的页面状态(增强版:包+Activity+结构) processed_elements = set() visited_page_hashes = set() # ================== 工具类:MobileElementActions ================== class MobileElementActions: def __init__(self, driver): self.driver = driver self.processed_inputs = set() def get_navigation_elements(self): all_clickable = self.get_all_clickable_elements() navigation_elements = [ele for ele in all_clickable if self.is_navigation_bar(ele)] return list(dict.fromkeys(navigation_elements)) def get_all_clickable_elements(self): return self.driver.find_elements( By.CSS_SELECTOR, ('button, a, input[type="button"], input[type="submit"], input[type="radio"], input[type="checkbox"], ' 'div[role="button"], [role="menuitem"], [role="option"], [data-testid*="button"], [class*="btn"], ' '[class*="clickable"], select') ) def is_navigation_bar(self, element): parent = element while True: try: parent = parent.find_element(By.XPATH, '..') if parent.tag_name.lower() == 'html': return False tag_name = parent.tag_name.lower() class_name = parent.get_attribute('class').lower() id_name = parent.get_attribute('id').lower() if tag_name == 'nav' or \ any(kw in class_name for kw in ['navbar', 'navigation', 'menu']) or \ any(kw in id_name for kw in ['nav', 'menu']): return True except Exception as e: return False def get_element_identifier(self, element): try: text = element.text.strip() id = element.get_attribute("id") tag = element.tag_name classes = ' '.join(element.get_attribute('class').split()) vaule = element.get_attribute("vaule") or ' ' return f"{tag}__{classes}__{text}__{vaule}__{id}" except: return str(hash(element)) def is_interactable(self, element): try: element_id = self.get_element_identifier(element) if element_id in processed_elements: return False if not element.is_displayed() or not element.is_enabled(): return False except: return str(hash(element)) def safe_click(self, element): """安全点击,支持 fallback 到坐标点击""" try: element.click() return True except ElementClickInterceptedException: print("⚠️ 元素被遮挡,尝试通过坐标点击") try: loc = element.location size = element.size x = loc['x'] + size['width'] // 2 y = loc['y'] + size['height'] // 2 self.driver.tap([(x, y)]) return True except Exception as tap_e: print(f"❌ 坐标点击也失败: {tap_e}") return False except Exception as e: print(f"❌ 点击失败: {e}") return False def wait_for_page_load(self, timeout=3): """等待页面加载完成""" time.sleep(timeout) # 可替换为显式等待动态内容 def handle_alerts(self): """自动关闭常见弹窗按钮""" alert_texts = ['OK', '确定', 'Cancel', '取消', '知道了', '同意'] for text in alert_texts: try: btn = self.driver.find_element( AppiumBy.XPATH, f"//*[@text='{text}' or @contentDescription='{text}']") if btn.is_displayed() and btn.is_enabled(): btn.click() print(f"✅ 自动关闭弹窗: {text}") return True except: continue return False def highlight_element(self, element, duration=1.5, color="yellow", border="2px solid red"): original_style = element.get_attribute("style") self.driver.execute_script( f"arguments[0].setAttribute('style', arguments[1]);", element, f"border: {border}; background-color: {color}; {original_style}" ) time.sleep(duration) self.driver.execute_script(f"arguments[0].setAttribute('style', arguments[1]);", element, original_style) def handle_input_fields(self): try: # 找输入框 inputs = WebDriverWait(self.driver, 5).until( EC.presence_of_all_elements_located( (By.CSS_SELECTOR, 'input[type="text"], input[type="password"], textarea')) ) for index, element in enumerate(inputs): try: # 处理输入框 element_id = self.get_element_identifier(element) if element_id in self.processed_inputs: continue if not element.is_displayed() or not element.is_enabled(): continue self.highlight_element(element, color="#ffff99", border="3px dashed red") print(f"请在输入框 {index + 1} 中输入内容。") time.sleep(1) entered_value = element.get_attribute("value") print(f"您在输入框 {index + 1} 中输入的内容为: {entered_value}") self.processed_inputs.add(element_id) except Exception as e: print(f"处理输入框时出错: {e}") except TimeoutException: pass except Exception as e: print(f"处理输入框时出错: {e}") def get_current_page_hash(self): """获取当前页面源码的哈希值""" try: src = self.driver.page_source return hashlib.md5(src.encode('utf-8')).hexdigest() except Exception as e: print(f"⚠️ 获取页面源码失败: {e}") return None def get_current_state_signature(self): """返回当前状态唯一标识:包名::Activity::页面哈希""" try: pkg = self.driver.current_package act = self.driver.current_activity hsh = self.get_current_page_hash() return f"{pkg}::{act}::{hsh}" except Exception as e: print(f"⚠️ 生成状态签名失败: {e}") return None # ================== 主流程:自动化遍历 ================== class MobileAutomationWorkflow: def __init__(self, driver): self.driver = driver self.actions = MobileElementActions(driver) self.max_iterations = 300 self.iteration_count = 0 def run(self): print("📱 开始移动应用自动化遍历...") try: while self.iteration_count < self.max_iterations: print(f"\n🔄 第 {self.iteration_count + 1} 轮:扫描当前页面") print("Current Activity:", self.driver.current_activity) # ✅ 检查是否仍在目标 App 中 if self.driver.current_package != TARGET_PACKAGE: print(f"⚠️ 当前不在目标应用 ({self.driver.current_package}),尝试返回...") try: self.driver.back() time.sleep(1.5) if self.driver.current_package != TARGET_PACKAGE: print("🔚 已退出目标应用,停止遍历") break except: print("🔙 返回失败,结束运行") break continue # ✅ 使用【包名+Activity+页面结构】作为唯一状态标识 current_state = self.actions.get_current_state_signature() if current_state in visited_page_hashes: print("🔁 当前【App+页面】组合已访问过,尝试返回") try: self.driver.back() time.sleep(1.5) continue except: print("🔙 返回失败或已达根页面") break else: visited_page_hashes.add(current_state) # 获取所有可见元素 try: elements = self.driver.find_elements(AppiumBy.XPATH, "//*") except Exception as e: print(f"❌ 获取元素列表失败: {e}") break print(f"找到 {len(elements)} 个元素") found_new_clickable = False for elem in elements: try: if not self.actions.is_interactable(elem): continue sig = self.actions.get_element_signature(elem) if not sig or sig in processed_elements: continue text = elem.get_attribute("text") or "No Text" desc = elem.get_attribute("contentDescription") or "" clazz = elem.get_attribute("className").split(".")[-1] print(f"👉 尝试点击: [{clazz}] '{text}' (描述: '{desc}')") if self.actions.safe_click(elem): processed_elements.add(sig) found_new_clickable = True self.actions.wait_for_page_load() # 处理弹窗 self.actions.handle_alerts() # 处理输入框 self.actions.handle_input_fields() # 成功点击后跳出,重新分析新页面 break except StaleElementReferenceException: print("⚠️ 元素已失效,跳过") continue except Exception as inner_e: print(f"⚠️ 处理元素时发生非致命错误: {inner_e}") continue if not found_new_clickable: print("✅ 当前页面无可点击的新元素") try: self.driver.back() time.sleep(1.5) back_state = self.actions.get_current_state_signature() if back_state in visited_page_hashes: print("🔚 回退后仍无法前进,结束遍历") break except Exception as back_e: print(f"🔙 返回失败或已达根页面: {back_e}") break self.iteration_count += 1 print(f"🏁 自动化完成,共执行 {self.iteration_count} 次操作。") except Exception as e: print(f"🚨 自动化过程发生严重错误: {e}") finally: self.driver.quit() # ================== 启动入口 ================== if __name__ == "__main__": workflow = MobileAutomationWorkflow(driver) workflow.run()
10-16
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值