我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android 10 适配攻略小结

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android 10 适配攻略小结

相比较去年写的Android 9适配,这次Android 10的内容有点多。没想到写了我整整两天,吐血中。。。

准备工作

老规矩,首先将我们项目中的

targetSdkVersion
改为 29。

1.Scoped Storage(分区存储) 说明

在Android 10之前的版本上,我们在做文件的操作时都会申请存储空间的读写权限。但是这些权限完全被滥用,造成的问题就是手机的存储空间中充斥着大量不明作用的文件,并且应用卸载后它也没有删除掉。为了解决这个问题,Android 10 中引入了

Scoped Storage
的概念,通过添加外部存储访问限制来实现更好的文件管理。

首先明确一个概念,外部储存和内部储存。

内部储存:
/data
目录。一般我们使用
getFilesDir()
getCacheDir()
方法获取本应用的内部储存路径,读写该路径下的文件不需要申请储存空间读写权限,且卸载应用时会自动删除。 外部储存:
/storage
/mnt
目录。一般我们使用
getExternalStorageDirectory()
方法获取的路径来存取文件。

因为不同厂商、系统版本的原因,所以上述的方法并没有一个固定的文件路径。了解了上面的概念,那我们所说的外部储存访问限制,可以认为是针对

getExternalStorageDirectory()
路径下的文件。具体的规则如下表:

上图将外部存储空间分为了三部分:

特定目录(App-specific),使用
getExternalFilesDir()
getExternalCacheDir()
方法访问。无需权限,且卸载应用时会自动删除。 照片、视频、音频这类媒体文件。使用
MediaStore
访问,访问其他应用的媒体文件时需要
READ_EXTERNAL_STORAGE
权限。 其他目录,使用 存储访问框架SAF (Storage Access Framwork)

所以在Android 10上即使你拥有了储存空间的读写权限,也无法保证可以正常的进行文件的读写操作。

适配

最简单粗暴的方法就是在

AndroidManifest.xml
中添加
android:requestLegacyExternalStorage="true"
来请求使用旧的存储模式。

但是我不推荐此方法。因为在下一个版本的Android中,此条配置将会失效,将强制采用外部储存限制。其实早在Android Q Beta 3之前都是强制的,但为了给开发者适配的时间才没有强制执行。所以如果你不抓住这段时间去适配,那么今年下半年出了Android 11。。。直接开花~~

如果你已经适配Android 10,这里有个现象要 注意一下

如果应用通过升级安装,那么还会使用以前的储存模式(Legacy View)。只有通过首次安装或是卸载重新安装才能启用新模式(Filtered View)。

所以在适配时,我们的判断代码如下:


 // 使用Environment.isExternalStorageLegacy()来检查APP的运行模式
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && 
    !Environment.isExternalStorageLegacy()) {
  }

这样的好处是你可以在用户升级后,能方便的将用户的数据移动至应用的特定目录。否则你只能通过SAF去移动,这样会非常麻烦。如果你要移动数据注意只适用于Android 10下,所以现在适配反而是一个好时机。当然如果你不需要迁移数据,那适配会更省事。

下面就说说推荐适配方案:

对于应用中涉及的文件操作,修改一下你的文件路径。

以前我们习惯使用

Environment.getExternalStorageDirectory()
方法,那么现在可以使用
getExternalFilesDir()
方法(包括下载的安装包这类的文件)。如果是缓存类型文件,可以放到
getExternalCacheDir()
路径下。

或者使用

MediaStore
,将文件存至对应的媒体类型中(图片:
MediaStore.Images
,视频:
MediaStore.Video
,音频:
MediaStore.Audio
),不过仅限于多媒体文件。

下面代码将图片保存到公共目录下,返回Uri:


public static Uri createImageUri(Context context) {
    ContentValues values = new ContentValues();
    // 需要指定文件信息时,非必须
    values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
    values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
    values.put(MediaStore.Images.Media.TITLE, "Image.png");
    values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");
    return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
  }

对于媒体资源的访问:比如图片选择器这类的场景。无法直接使用File,而应使用Uri。否则报错如下:


java.io.FileNotFoundException: open failed: EACCES (Permission denied)

比如我在适配项目中使用的图片选择器时,首先修改了

Glide
通过加载File的方式显示图片。改为加载Uri的方式,否则图片无法显示出来。

Uri的获取方式还是使用

MediaStore


String id = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);

其次为了便于不影响之前选择图片返回File的逻辑(因为一般都是上传File,没有直接上传Uri的操作),所以我将最终选择的文件又转存进了

getExternalFilesDir()
,主要代码如下:


  File imgFile = this.getExternalFilesDir("image");
  if (!imgFile.exists()){
    imgFile.mkdir();
  }
  try {
    File file = new File(imgFile.getAbsolutePath() + File.separator + 
    	System.currentTimeMillis() + ".jpg");
    // 使用openInputStream(uri)方法获取字节输入流
    InputStream fileInputStream = getContentResolver().openInputStream(uri);
    FileOutputStream fileOutputStream = new FileOutputStream(file);
    byte[] buffer = new byte[1024];
    int byteRead;
    while (-1 != (byteRead = fileInputStream.read(buffer))) {
      fileOutputStream.write(buffer, 0, byteRead);
    }
    fileInputStream.close();
    fileOutputStream.flush();
    fileOutputStream.close();
    // 文件可用新路径 file.getAbsolutePath()
  } catch (Exception e) {
    e.printStackTrace();    
  }

如果你要获取图片中的地理位置信息,需要申请

ACCESS_MEDIA_LOCATION
权限,并使用MediaStore.setRequireOriginal()获取。下面是官方的示例代码:


 Uri photoUri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
		 cursor.getString(idColumnIndex));
  final double[] latLong;
  // 从ExifInterface类获取位置信息
  photoUri = MediaStore.setRequireOriginal(photoUri);
  InputStream stream = getContentResolver().openInputStream(photoUri);
  if (stream != null) {
    ExifInterface exifInterface = new ExifInterface(stream);
    double[] returnedLatLong = exifInterface.getLatLong();
    // If lat/long is null, fall back to the coordinates (0, 0).
    latLong = returnedLatLong != null ? returnedLatLong : new double[2];
    // Don't reuse the stream associated with the instance of "ExifInterface".
    stream.close();
  } else {
    // Failed to load the stream, so return the coordinates (0, 0).
    latLong = new double[2];
  }

这样下来,一个图片选择器就基本适配完了。

补充

应用在卸载后,会将

App-specific
目录下的数据删除,如果在
AndroidManifest.xml
中声明:
android:hasFragileUserData="true"
用户可以选择是否保留。

对于

SAF
的使用,可以查看我之前写的 SAF使用攻略 ,这里就不展开说了。

最后这里有一个介绍Scoped Storage的视频,推荐 观看 :

2.权限变化

从6.0开始,基本每次都会有权限方面变动,这次也不例外。(前几天发布了Android 11的预览版,看来也有权限方面的变化。。。单次权限即将到来)

1.在后台运行时访问设备位置信息需要权限

Android 10 引入了

ACCESS_BACKGROUND_LOCATION
权限(危险权限)。


<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

该权限允许应用程序在后台访问位置。如果请求此权限,则还必须请求

ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
权限。只请求此权限无效果。

在Android 10的设备上,如果你的应用的

targetSdkVersion
< 29,则在请求
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
权限时,系统会自动同时请求
ACCESS_BACKGROUND_LOCATION
。在请求弹框中,选择“始终允许”表示同意后台获取位置信息,选择“仅在应用使用过程中允许”或"拒绝"选项表示拒绝授权。

如果你的应用的

targetSdkVersion
>= 29,则请求
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
权限表示在前台时拥有访问设备位置信息的权。在请求弹框中,选择“始终允许”表示前后台都可以获取位置信息,选择“仅在应用使用过程中允许”只表示拥有前台的权限。

总结一下就是下图:

 

其实官方 不推荐你使用申请后台访问权的方式 ,因为这样的结果无非就是多请求一个权限,那么这像变更还有什么意义?申请过多的权限,也会造成用户的反感。所以官方推荐使用 前台服务

来实现,在前台服务中获取位置信息。

首先在清单中对应的

service
中添加
android:foregroundServiceType="location"


  <service
    android:name="MyNavigationService"
    android:foregroundServiceType="location" ... >
    ...
  </service>

启动前台服务前检查是否具有前台的访问权限:


  boolean permissionApproved = ActivityCompat.checkSelfPermission(this, 
		Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
  if (permissionApproved) {
    // 启动前台服务
  } else {
    // 请求前台访问位置权限
  }

如此一来就可以在

Service
中获取位置信息。

2.一些电话、蓝牙和WLAN的API需要精确位置权限

下面列举了Android 10中必须具有

ACCESS_FINE_LOCATION
权限才能使用类和方法:

电话

 TelephonyManager getCellLocation() getAllCellInfo() requestNetworkScan() requestCellInfoUpdate() getAvailableNetworks() getServiceState() TelephonyScanManager requestNetworkScan() TelephonyScanManager.NetworkScanCallback onResults() PhoneStateListener onCellLocationChanged() onCellInfoChanged() onServiceStateChanged()

WLAN

 WifiManager startScan() getScanResults() getConnectionInfo() getConfiguredNetworks() WifiAwareManager WifiP2pManager WifiRttManager

蓝牙

 BluetoothAdapter startDiscovery() startLeScan() BluetoothAdapter.LeScanCallback BluetoothLeScanner startScan()

我们可以根据上面提供的具体类和方法,在适配项目中检查是否有使用到并及时处理。

3.ACCESS_MEDIA_LOCATION
Android 10新增权限,上面有提到,不赘述了。

4.PROCESS_OUTGOING_CALLS
Android 10上该权限已废弃。

3.后台启动 Activity 的限制

简单解释就是 应用处于后台时,无法启动Activity 。比如点开一个应用会进入启动页或者广告页,一般会有几秒的延时再跳转至首页。如果这期间你退到后台,那么你将无法看到跳转过程。而在之前的版本中,会强制弹出页面至前台。

既然是限制,那么肯定有不受限的情况,主要有以下几点:

应用具有可见窗口,例如前台 Activity。 应用在前台任务的返回栈中已有的 Activity。 应用在
Recents
上现有任务的返回栈中已有的 Activity。
Recents
就是我们的任务管理列表。 应用收到系统的
PendingIntent
通知。 应用收到它应该在其中启动界面的系统广播。示例包括
ACTION_NEW_OUTGOING_CALL
SECRET_CODE_ACTION
。应用可在广播发送几秒钟后启动 Activity。 用户已向应用授予
SYSTEM_ALERT_WINDOW
权限,或是在应用权限页开启
后台弹出页面
的开关。

因为此项行为变更适用于在 Android 10 上运行的所有应用,所以这一限制导致最明显的问题就是点击推送信息时,有些应用无法进行正常的跳转(具体的实现问题导致)。所以针对这类问题,可以采取

PendingIntent
的方式,发送通知时使用
setContentIntent
方法。

当然你也可以申请相应权限或者白名单:

不过申请白名单这种方法受各种手机厂商所限,很麻烦。感觉还不如引导用户手动开启权限。。。

对于全屏 intent,注意设置最高优先级和添加

USE_FULL_SCREEN_INTENT
权限,这是一个普通权限。比如微信来语音或者视频通话时,弹出的接听页面就是使用这一功能。


 <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>

Intent fullScreenIntent = new Intent(this, CallActivity.class);
  PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
      fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
  NotificationCompat.Builder notificationBuilder =
      new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("Incoming call")
    .setContentText("(919) 555-1234")
    .setPriority(NotificationCompat.PRIORITY_HIGH) // <--- 高优先级
    .setCategory(NotificationCompat.CATEGORY_CALL)
    // Use a full-screen intent only for the highest-priority alerts where you
    // have an associated activity that you would like to launch after the user
    // interacts with the notification. Also, if your app targets Android 10
    // or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
    // order for the platform to invoke this notification.
    .setFullScreenIntent(fullScreenPendingIntent, true); // <--- 全屏 intent
  Notification incomingCallNotification = notificationBuilder.build();

注意:在部分手机上,直接设置

setPriority
无效(或者说以渠道优先级为准)。所以需要创建通知渠道时将重要性设置为
IMPORTANCE_HIGH


NotificationChannel channel = new NotificationChannel(channelId, "xxx", NotificationManager.IMPORTANCE_HIGH);

后台启动 Activity 的限制的目的是为了减少对用户操作的中断。如果你有要弹出的页面,推荐你先弹出通知,让用户自己选择接下来的操作,而不是一股脑的强制弹出。(如果你的全屏intent都让用户反感,那他也可以关掉你的通知,不至于任你摆布。)

4.深色主题

Android 10 新增了一个系统级的深色主题(在系统设置中开启)。虽然深色主题并不是强制适配项,但是它可以带给用户更好的体验:

可大幅减少耗电量。
OLED
屏幕中每个像素都是自主发光,所以在显示深色元素时像素所消耗的电流更低,尤其在纯黑颜色时像素点可以完全关闭来达到省电的效果。 为弱视以及对强光敏感的用户提高可视性。深色可以降低屏幕的整体视觉亮度,减少对眼睛的视觉压力。 让所有人都可以在光线较暗的环境中更轻松地使用设备。

适配方法有两种:

1.手动适配(资源替换)

官方文档中提到的继承

Theme.AppCompat.DayNight
或者
Theme.MaterialComponents.DayNight
的方法,但这只是将我们使用的各种View的默认样式进行了适配,并不太适用于实际项目的适配。因为具体的项目中的View都按照设计的风格进行了重定义。

其实适配的方法很简单,类似屏幕适配、国际化的操作,并不需要继承上面的主题。比如你要修改颜色,就在

res
下新建
values-night
目录,创建对应的
colors.xml
文件。将具体要修改的色值定义在里面。图标之类的也是一个思路,创建对应的
drawable-night
目录。

只要你之前的代码不是硬编码且代码规范,那么适配起来还是很轻松。

2.自动适配(Force Dark)

Android 10 提供 Force Dark 功能。一如其名,此功能可让开发者快速实现深色主题背景,而无需明确设置 DayNight 主题背景。

如果您的应用采用浅色主题背景,则 Force Dark 会分析应用的每个视图,并在相应视图在屏幕上显示之前,自动应用深色主题背景。有些开发者会混合使用 Force Dark 和本机实现,以缩短实现深色主题背景所需的时间。

应用必须选择启用 Force Dark,方法是在其主题背景中设置

android:forceDarkAllowed="true"
。此属性会在所有系统及 AndroidX 提供的浅色主题背景(例如 Theme.Material.Light)上设置。使用 Force Dark 时,您应确保全面测试应用,并根据需要排除视图。

如果您的应用使用

Dark Theme
主题(例如Theme.Material),则系统不会应用 Force Dark。同样,如果应用的主题背景继承自
DayNight
主题(例如Theme.AppCompat.DayNight),则系统不会应用 Force Dark,因为会自动切换主题背景。

您可以通过

android:forceDarkAllowed
布局属性或
setForceDarkAllowed(boolean)
在特定视图上控制 Force Dark。

上述内容我直接照搬文档的说明。总结一下,使用

Force Dark
需要注意几点:

如果使用的是
DayNight
Dark Theme
主题,则设置
forceDarkAllowed
不生效。 如果有需要排除适配的部分,可以在对应的View上设置
forceDarkAllowed
为false。

这里说说我实际使用此方法的感受: 整体还是不错的,设置的色值会自动取反。但也因此颜色不受控制,能否达到预期效果是个需要注意的问题。追求快速适配可以采取此方案。

手动切换主题

使用

AppCompatDelegate.setDefaultNightMode(@NightMode int mode)
方法,其中参数
mode
有以下几种:

浅色 - MODE_NIGHT_NO 深色 - MODE_NIGHT_YES 由省电模式设置 - MODE_NIGHT_AUTO_BATTERY 系统默认 - MODE_NIGHT_FOLLOW_SYSTEM

下面的代码是官方Demo中的使用示例:


public class ThemeHelper {
  public static final String LIGHT_MODE = "light";
  public static final String DARK_MODE = "dark";
  public static final String DEFAULT_MODE = "default";
  public static void applyTheme(@NonNull String themePref) {
    switch (themePref) {
      case LIGHT_MODE: {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
        break;
      }
      case DARK_MODE: {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
        break;
      }
      default: {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
        } else {
          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
        }
        break;
      }
    }
  }
}

通过

AppCompatDelegate.getDefaultNightMode()
方法,可以获取到当前的模式,这样便于代码中去适配。

监听深色主题是否开启

首先在清单文件中给对应的Activity配置

android:configChanges="uiMode"


 <activity
  	android:name=".MyActivity"
  	android:configChanges="uiMode" />

这样在

onConfigurationChanged
方法中就可以获取:


	@Override
  public void onConfigurationChanged(@NonNull Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
    switch (currentNightMode) {
      case Configuration.UI_MODE_NIGHT_NO:
        // 关闭
        break;
      case Configuration.UI_MODE_NIGHT_YES:
        // 开启
        break;
      default:
        break;  
    }
  }

详细的内容你可以参看官方文档 和官方Demo 。

判断深色主题是否开启

其实和上面

onConfigurationChanged
方法同理:


  public static boolean isNightMode(Context context) {
    int currentNightMode = context.getResources().getConfiguration().uiMode & 
    	Configuration.UI_MODE_NIGHT_MASK;
    return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
  }
5.标识符和数据

 对不可重置的设备标识符实施了限制

受影响的方法包括:

Build getSerial() TelephonyManager getImei() getDeviceId() getMeid() getSimSerialNumber() getSubscriberId()

从 Android 10 开始,应用必须具有

READ_PRIVILEGED_PHONE_STATE
特许权限才能正常使用以上这些方法。

如果你的应用没有该权限,却仍然使用了以上的方法,则返回的结果会因目标 SDK 版本而异:

如果应用以 Android 10 或更高版本为目标平台 ,则会发生
SecurityException
。  如果应用以 Android 9(API 级别 28)或更低版本为目标平台 ,则相应方法会返回 null 或占位符数据(如果应用具有
READ_PHONE_STATE
权限)。否则,会发生
SecurityException

这项改动表示第三方应用无法获取

Device ID
这类唯一标识。如果你需要唯一标识符,请参阅文档: 唯一标识符的最佳做法 。

当然你也可以试试移动安全联盟(MSA)联合多家厂商共同开发的 统一补充设备标识调用SDK 。据说还有点不稳定,因为我暂时还没有尝试过,所以不做评价。

限制了对剪贴板数据的访问权限

除非您的应用是默认输入法 (IME) 或是目前处于焦点的应用,否则它无法访问 Android 10 或更高版本平台上的剪贴板数据。

对启用和停用 WLAN 实施了限制

以 Android 10 或更高版本为目标平台的应用无法启用或停用 WLAN。

WifiManager.setWifiEnabled()方法始终返回 false。

如果您需要提示用户启用或停用 WLAN,请使用设置面板。

6.其他

Android10上对折叠屏设备有了更好的支持,对于有折叠屏适配的需求,可以参看为可折叠设备构建应用 和 华为折叠屏应用开发指导。

以上内容只是Android 10中比较大的几项变化,完整的内容可以查看官方文档。

参考

OPPO - Android Q版本应用兼容性适配指导

面向开发者的 Android 10

用阿里巴巴APP的案例,教你如何快速适配「深色模式」

到此这篇关于Android 10 适配攻略小结的文章就介绍到这了,更多相关Android 10 适配内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文章:Android10填坑适配指南(实际经验代码)AndroidQ(10)分区存储完美适配方法


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android 10 适配攻略小结

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android 10 适配攻略小结

相比较去年写的Android 9适配,这次Android 10的内容有点多。没想到写了我整整两天,吐血中。。。 准备工作 老规矩,首先将我们项目中的 targetSdkVersion 改为 29。 1.Scoped Storage(分区存储
2022-06-06

iOS15适配小结

目录1、tabbar及navicationbar的背景颜色问题原因:因为设置颜色方法在ios15中失效解决方法--重新设置相关属性2、tableview新属性-sectionHeaderTopPadding使用1、tabbar及navica
2022-05-27

iOS13原生端适配攻略(推荐)

目录1. KVC访问私有属性2. 模态弹窗 ViewController 默认样式改变3. 黑暗模式的适配4. LaunchImage即将废弃5. 新增一直使用蓝牙的权限申请6. Sign With Apple7. 推送Device Tok
2022-05-23

Android 屏幕适配总结

本文主要涉及以下几篇文章 Android 屏幕适配总结 Android ConstraintLayout 使用与适配(使用篇) Android ConstraintLayout 使用与适配(适配篇) 目录 一、与屏幕相关的概念 1. 屏幕尺
2022-06-06

iPhoneX 序列适配方案(小结)

和往常一样,苹果发布新产品,我们作为开发者都需要对系统和UI布局进行适配,今年也是一样。从去年发布的 iphoneX开始,iPhone 手机加入了刘海设计,而且针对于iphone的刘海,需要特殊的适配。今年新出的3款iphone都带有刘海,
2022-05-29

iOS13 适配和Xcode11.0踩坑小结

iOS13中presentViewController的问题 更新了Xcode11.0 beta之后,在iOS13中运行代码发现presentViewController和之前弹出的样式不一样。会出现这种情况是主要是因为我们之前对UIVie
2022-06-04

Tomcat配置必备的10个小技巧用法总结

Tomcat具有免费、跨平台等诸多特性,并且更新得很快,现在非常的流行,你所需要做的就是:按照你的需求配置Tomcat,只要你正确配置,Tomcat一般都能适合你的要求,下面是一系列关于Tomcat的配置技巧,希望对你有所帮助
2023-05-19

Android-屏幕适配需要注意的地方总结

1.尽量使用线性布局(LinearLayout)和相对布局(RelativeLayout),不要使用绝对布局。 2.尽量使用dip和sp,不要使用px。 3.为不同的分辨率提供不同的布局文件和图片。 例如: 4.在AndroidMainf
2022-06-06

Android编程实现WebView自适应全屏方法小结

本文实例讲述了Android编程实现WebView自适应全屏的方法。分享给大家供大家参考,具体如下: 第一种:settings.setUseWideViewPort(true); settings.setLoadWithOverviewMo
2022-06-06

【Android】小米手机 Root全攻略:轻松获取手机最高权限

▒ 目录 ▒ 🛫 导读需求开发环境 1️⃣ 备份手机资源小米云服务小米助手备份方式 2️⃣ 解锁BL开启解锁`等168个小时(七天)`miflash_unlock解锁设备 3️⃣ 获取并安装MIUI
2023-08-20

Android中TextView自动适配文本大小的几种解决方案

目录TextView文本大小自动适配与TextView边距的去除一、Autosizing的方式(固定宽度)二、自定义View的方式(固定宽度)三、使用工具类自行计算(非控件固定宽度)四、去除TextView的边距总结TextView文本大小
Android中TextView自动适配文本大小的几种解决方案
2022-06-09

Android中TextView自动适配文本大小的解决方案有哪些

本篇内容介绍了“Android中TextView自动适配文本大小的解决方案有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、Autos
2023-07-02

Android中关于屏幕的三个小众知识(宽屏适配、禁止截屏和保持屏幕常亮)

前言宽屏适配、禁止截屏和保持屏幕常亮,这三个与屏幕有关的 Android 开发小众知识,说不定什么时候就派上用场。宽屏适配Android的屏幕适配一直以来都在折磨着我们Android开发者,越来越多的手机厂商趋向于全面屏设计,比如今年出厂的
2023-05-30

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录