Metrics_and_Grids

设备之间除了屏幕尺寸不同,屏幕的像素密度 (DPI) 也不一样。为了简化您为不同的屏幕设计 UI 的复杂度,可以将所有的设备按照大小和像素密度如下分类。按设备大小分有两类,分别是手持设备 (小于 600 dp) 和 平板 (大于等于 600dp)。而像素密度的类别有 LDPI、MDPI、HDPI 和 XHDPI。您应该为不同的设备使用不一样的布局文件来优化界面,以及为各种像素密度提供大小不同的位图。

屏幕大小的数据

不同的设备可显示的 dp (density-independent pixels) 数量也不相同。

更多信息请访问最新的 Android 各个版本系统的使用统计和屏幕大小

48dp 定律 - 48dp Rhythm

总的来说,可触摸控件都是以 48dp 为单位的。

为什么是 48dp?

一般情况下,48dp 在设备上的物理大小是 9mm (会有一些浮动)。这是触摸控件的推荐大小 (范围7-10mm) ,用户用手指触摸起来比较容易、且准确。

如果您设计的 UI 元素都至少有 48dp 的高度和宽度,那么可以保证:

    • 您设计的元素在任何屏幕上显示时,都不会小于推荐的最低值 7mm。
    • 你可以在信息密度和界面的可操控性之间得到较好的平衡。

注意留白

界面元素之间的留白应当是 8dp。

一个例子 - Examples

 

 

调用此方法,仍然无法检测到应用页面部分区域短时间闪烁或者黑屏有画面这样闪烁几次,无法检测到此现象,方法如下: def screen_flash_detection( self, monitor_idx=0, detect_fps=15, diff_threshold=30, flash_freq_threshold=3, min_flash_duration=0.1, grid_size=8 ): sct = mss.mss() monitor = sct.monitors[int(monitor_idx)] width, height = monitor["width"], monitor["height"] grid_rows = grid_cols = grid_size grid_h, grid_w = height // grid_rows, width // grid_cols # 修复1:确保网格索引正确计算 total_grids = grid_rows * grid_cols prev_grids = None flash_count = 0 start_time = time.time() flash_detected = False print(f"开始分区闪屏检测({grid_rows}x{grid_cols}网格)...") while not (keyboard.is_pressed('q') or flash_detected): frame_start = time.time() # 屏幕捕获处理(保持之前的修正) sct_img = sct.grab(monitor) try: img_bytes = bytes(sct_img.raw) curr_frame = np.frombuffer( img_bytes, dtype=np.uint8 ).reshape((sct_img.height, sct_img.width, 4)) except AttributeError: from mss.tools import to_png png_data = to_png(sct_img.rgb, sct_img.size) curr_frame = cv2.imdecode( np.frombuffer(png_data, np.uint8), cv2.IMREAD_COLOR ) curr_frame = curr_frame[:, :, :3] curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY) # 修复2:重构网格处理逻辑 curr_grids = [] grid_flash_count = 0 # 确保网格索引在范围内 for idx in range(total_grids): i = idx // grid_cols # 行索引 j = idx % grid_cols # 列索引 # 计算网格边界 y1 = i * grid_h y2 = min((i + 1) * grid_h, height) # 防止越界 x1 = j * grid_w x2 = min((j + 1) * grid_w, width) # 防止越界 grid = curr_gray[y1:y2, x1:x2] curr_grids.append(grid) # 跳过第一帧的比较 if prev_grids is None: continue # 修复3:添加索引保护 if idx < len(prev_grids): prev_grid = prev_grids[idx] diff = np.mean(np.abs(grid.astype(np.float32) - prev_grid.astype(np.float32))) if diff > diff_threshold: grid_flash_count += 1 cv2.rectangle(curr_gray, (x1, y1), (x1 + 15, y1 + 15), (0, 0, 0), -1) cv2.rectangle(curr_gray, (x1, y1), (x1 + 10, y1 + 10), (0, 0, 255), -1) # 更新前一帧网格数据 prev_grids = [g.copy() for g in curr_grids] # 闪屏判定逻辑 if prev_grids and grid_flash_count > 0: flash_count += grid_flash_count cv2.putText( curr_gray, f"FLASH: {grid_flash_count}区域", (width // 2 - 100, height // 2), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2 ) elapsed_time = time.time() - start_time if elapsed_time >= min_flash_duration: flash_freq = flash_count / elapsed_time if flash_freq >= flash_freq_threshold: flash_detected = True print(f"⚠️ 检测到局部闪屏!影响区域: {grid_flash_count}/{total_grids}") # 显示和退出逻辑 display_frame = cv2.resize(curr_gray, (0, 0), fx=0.5, fy=0.5) cv2.imshow("分区闪屏检测", display_frame) key = cv2.waitKey(1) & 0xFF if key == ord('q') or cv2.getWindowProperty("分区闪屏检测", cv2.WND_PROP_VISIBLE) < 1: break frame_time = time.time() - frame_start delay = max(1, int(1000 / detect_fps - frame_time * 1000)) if delay > 0: cv2.waitKey(delay) cv2.destroyAllWindows() sct.close() print("检测结束") return flash_detected
10-14
def deviations_sample_culation(height_deviations): deviation_sum = 0 height_deviations_count = len(height_deviations) #高度偏差计数 deviation_average = sum(height_deviations)/height_deviations_count # 偏差平均数 for deviation in height_deviations: deviation_sum += (deviation-deviation_average)**2 deviations_sample = deviation_sum/(height_deviations_count)#高度偏差的方差 return deviations_sample def compute_min_variance(grids,blocks): """ 请在此处实现处理逻辑 参数grids是一个M✖N的二维数组(double 类型) 参数blocks是一个K列的一维数组(double 类型) 返回 最小的磁钢高度方差值 """ grids = [grid for row in grids for grid in row] grids_count = len(grids) #平面格子的数量 grids_sorted = sorted(grids)#对格子的高度升序 blocks_count = len(blocks) #小磁钢的数量 blocks_sorted = sorted(blocks, reverse=True) height_deviations = [float(0) for i in range(grids_count)] blocks_sorted_list=[] def back(start, path): # 终止条件:满足长度要求 if len(path) == grids_count: blocks_sorted_list.append(path[:]) return for i in range(start, len(blocks_sorted)): # 去重:同一层跳过重复元素 if i > start and blocks_sorted[i] == blocks_sorted[i - 1]: continue # 选择当前元素 path.append(blocks_sorted[i]) # 递归:从下一位置继续选择 back(i + 1, path) # 回溯:撤销选择 path.pop() back(0, []) variances = [] if grids_count == 0 : height_deviations = [] else: for blocks in blocks_sorted_list: for i in range (grids_count): height_deviations[i] = grids_sorted[i]+ blocks[i] variances.append(deviations_sample_culation(height_deviations)) return min(variances) 处理一下边界值可能的错误
09-04
UnicodeEncodeError Traceback (most recent call last) Cell In[3], line 146 137 if name in param_grids: 138 grid_search = GridSearchCV( 139 estimator=model, 140 param_grid=param_grids[name], (...) 144 verbose=1 145 ) --> 146 grid_search.fit(X_train, y_train) 148 best_models[name] = grid_search.best_estimator_ 149 print(f"Best params: {grid_search.best_params_}") File ~\AppData\Roaming\Python\Python312\site-packages\sklearn\base.py:1365, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs) 1358 estimator._validate_params() 1360 with config_context( 1361 skip_parameter_validation=( 1362 prefer_skip_nested_validation or global_skip_validation 1363 ) 1364 ): -> 1365 return fit_method(estimator, *args, **kwargs) File ~\AppData\Roaming\Python\Python312\site-packages\sklearn\model_selection\_search.py:979, in BaseSearchCV.fit(self, X, y, **params) 967 fit_and_score_kwargs = dict( 968 scorer=scorers, 969 fit_params=routed_params.estimator.fit, (...) 976 verbose=self.verbose, 977 ) 978 results = {} --> 979 with parallel: 980 all_candidate_params = [] 981 all_out = [] File D:\anacondaxiaz\Lib\site-packages\joblib\parallel.py:1347, in Parallel.__enter__(self) 1345 self._managed_backend = True 1346 self._calling = False -> 1347 self._initialize_backend() 1348 return self File D:\anacondaxiaz\Lib\site-packages\joblib\parallel.py:1359, in Parallel._initialize_backend(self) 1357 """Build a process or thread pool and return the number of workers""" 1358 try: -> 1359 n_jobs = self._backend.configure(n_jobs=self.n_jobs, parallel=self, 1360 **self._backend_args) 1361 if self.timeout is not None and not self._backend.supports_timeout: 1362 warnings.warn( 1363 'The backend class {!r} does not support timeout. ' 1364 "You have set 'timeout={}' in Parallel but " 1365 "the 'timeout' parameter will not be used.".format( 1366 self._backend.__class__.__name__, 1367 self.timeout)) File D:\anacondaxiaz\Lib\site-packages\joblib\_parallel_backends.py:538, in LokyBackend.configure(self, n_jobs, parallel, prefer, require, idle_worker_timeout, **memmappingexecutor_args) 534 if n_jobs == 1: 535 raise FallbackToBackend( 536 SequentialBackend(nesting_level=self.nesting_level)) --> 538 self._workers = get_memmapping_executor( 539 n_jobs, timeout=idle_worker_timeout, 540 env=self._prepare_worker_env(n_jobs=n_jobs), 541 context_id=parallel._id, **memmappingexecutor_args) 542 self.parallel = parallel 543 return n_jobs File D:\anacondaxiaz\Lib\site-packages\joblib\executor.py:20, in get_memmapping_executor(n_jobs, **kwargs) 19 def get_memmapping_executor(n_jobs, **kwargs): ---> 20 return MemmappingExecutor.get_memmapping_executor(n_jobs, **kwargs) File D:\anacondaxiaz\Lib\site-packages\joblib\executor.py:42, in MemmappingExecutor.get_memmapping_executor(cls, n_jobs, timeout, initializer, initargs, env, temp_folder, context_id, **backend_args) 39 reuse = _executor_args is None or _executor_args == executor_args 40 _executor_args = executor_args ---> 42 manager = TemporaryResourcesManager(temp_folder) 44 # reducers access the temporary folder in which to store temporary 45 # pickles through a call to manager.resolve_temp_folder_name. resolving 46 # the folder name dynamically is useful to use different folders across 47 # calls of a same reusable executor 48 job_reducers, result_reducers = get_memmapping_reducers( 49 unlink_on_gc_collect=True, 50 temp_folder_resolver=manager.resolve_temp_folder_name, 51 **backend_args) File D:\anacondaxiaz\Lib\site-packages\joblib\_memmapping_reducer.py:540, in TemporaryResourcesManager.__init__(self, temp_folder_root, context_id) 534 if context_id is None: 535 # It would be safer to not assign a default context id (less silent 536 # bugs), but doing this while maintaining backward compatibility 537 # with the previous, context-unaware version get_memmaping_executor 538 # exposes too many low-level details. 539 context_id = uuid4().hex --> 540 self.set_current_context(context_id) File D:\anacondaxiaz\Lib\site-packages\joblib\_memmapping_reducer.py:544, in TemporaryResourcesManager.set_current_context(self, context_id) 542 def set_current_context(self, context_id): 543 self._current_context_id = context_id --> 544 self.register_new_context(context_id) File D:\anacondaxiaz\Lib\site-packages\joblib\_memmapping_reducer.py:569, in TemporaryResourcesManager.register_new_context(self, context_id) 562 new_folder_name = ( 563 "joblib_memmapping_folder_{}_{}_{}".format( 564 os.getpid(), self._id, context_id) 565 ) 566 new_folder_path, _ = _get_temp_dir( 567 new_folder_name, self._temp_folder_root 568 ) --> 569 self.register_folder_finalizer(new_folder_path, context_id) 570 self._cached_temp_folders[context_id] = new_folder_path File D:\anacondaxiaz\Lib\site-packages\joblib\_memmapping_reducer.py:585, in TemporaryResourcesManager.register_folder_finalizer(self, pool_subfolder, context_id) 578 def register_folder_finalizer(self, pool_subfolder, context_id): 579 # Register the garbage collector at program exit in case caller forgets 580 # to call terminate explicitly: note we do not pass any reference to 581 # ensure that this callback won't prevent garbage collection of 582 # parallel instance and related file handler resources such as POSIX 583 # semaphores and pipes 584 pool_module_name = whichmodule(delete_folder, 'delete_folder') --> 585 resource_tracker.register(pool_subfolder, "folder") 587 def _cleanup(): 588 # In some cases the Python runtime seems to set delete_folder to 589 # None just before exiting when accessing the delete_folder (...) 594 # because joblib should only use relative imports to allow 595 # easy vendoring. 596 delete_folder = __import__( 597 pool_module_name, fromlist=['delete_folder'] 598 ).delete_folder File D:\anacondaxiaz\Lib\site-packages\joblib\externals\loky\backend\resource_tracker.py:179, in ResourceTracker.register(self, name, rtype) 177 """Register a named resource, and increment its refcount.""" 178 self.ensure_running() --> 179 self._send("REGISTER", name, rtype) File D:\anacondaxiaz\Lib\site-packages\joblib\externals\loky\backend\resource_tracker.py:196, in ResourceTracker._send(self, cmd, name, rtype) 192 if len(name) > 512: 193 # posix guarantees that writes to a pipe of less than PIPE_BUF 194 # bytes are atomic, and that PIPE_BUF >= 512 195 raise ValueError("name too long") --> 196 msg = f"{cmd}:{name}:{rtype}\n".encode("ascii") 197 nbytes = os.write(self._fd, msg) 198 assert nbytes == len(msg) UnicodeEncodeError: 'ascii' codec can't encode characters in position 18-19: ordinal not in range(128)
08-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值