我正在尝试拥有一个具有不同视图类型的 RecyclerView,但具有拖放功能以及单击和长按操作的能力。
它类似于您在电话应用程序上的功能,您可以在其中更改收藏夹项目的顺序。在电话应用程序中,当您长按某个项目时,会立即出现一个上下文菜单,如果您继续拖动,上下文菜单就会消失。
但是,在这种情况下,我需要做相反的事情。长按时,如果用户在很短的时间内没有拖动,或者用户在没有拖动的情况下停止长按,我们会在屏幕上显示一个对话框,我需要停止拖动过程。
虽然我已经成功地处理了长触摸机制,并且我在这些特殊情况下显示了一个对话框,但我未能使拖动停止。
这意味着,如果用户在对话框出现后继续触摸屏幕,仍然可以继续拖动:
整个代码在这里可用(没有长触摸行为的代码在这里可用),但这里是主要代码:
class MainActivity : AppCompatActivity() {
sealed class Item(val id: Long, val itemType: Int) {
class HeaderItem(id: Long) : Item(id, ITEM_TYPE_HEADER)
class NormalItem(id: Long, val data: Long) : Item(id, 1)
}
enum class ItemActionState {
IDLE, LONG_TOUCH_OR_SOMETHING_ELSE, DRAG, SWIPE, HANDLED_LONG_TOUCH
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val items = ArrayList<Item>(100)
var itemDataCounter = 0L
items.add(Item.HeaderItem(0L))
for (i in 0 until 100) {
items.add(Item.NormalItem(itemDataCounter, itemDataCounter))
++itemDataCounter …Run Code Online (Sandbox Code Playgroud) 使用 root,我知道对于单个 APK 文件,我们可以使用“libsu”库(此处)进行安装:
val installResult = Shell.su("pm install -t \"$filePath\"").exec()
Run Code Online (Sandbox Code Playgroud)
如果失败了(在新的 Android 版本上失败了,不确定是哪个版本),如下(在这里写下):
val installResult = Shell.su("cat \"$filePath\" | pm install -t -S ${apkSource.fileSize}").exec()
Run Code Online (Sandbox Code Playgroud)
我还知道,在安装拆分 APK 文件时,事情会变得非常混乱(如此处所示)。首先,您需要使用“pm install-create”命令创建一个会话:
var sessionId: Int? = null
run {
val sessionIdResult =
Shell.su("pm install-create -r -t").exec().out
val sessionIdPattern = Pattern.compile("(\\d+)")
val sessionIdMatcher = sessionIdPattern.matcher(sessionIdResult[0])
sessionIdMatcher.find()
sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!)
Log.d("AppLog", "sessionId:$sessionId")
}
Run Code Online (Sandbox Code Playgroud)
然后你必须“推送”每个 APK 文件,如下所示:
for (apkSource in fileInfoList) {
val filePath = File(apkSource.parentFilePath, apkSource.fileName).absolutePath
Log.d("AppLog", "installing …Run Code Online (Sandbox Code Playgroud) android adb android-install-apk android-app-bundle android-10.0
我需要解析一些各种类型的 zip 文件(出于某种目的获取一些内部文件内容,包括获取它们的名称)。
有些文件无法通过文件路径访问,因为 Android 有 Uri 来访问它们,并且有时 zip 文件位于另一个 zip 文件内。随着使用 SAF 的推动,在某些情况下使用文件路径的可能性甚至更小。
为此,我们有 2 个主要方法来处理:ZipFile类和ZipInputStream类。
当我们有文件路径时,ZipFile 是一个完美的解决方案。在速度方面它也非常有效。
然而,对于其余情况,ZipInputStream 可能会遇到问题,例如这个zip 文件有问题,并导致此异常:
java.util.zip.ZipException: only DEFLATED entries can have EXT descriptor
at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:321)
at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:124)
Run Code Online (Sandbox Code Playgroud)
唯一始终有效的解决方案是将文件复制到其他地方,您可以在其中使用 ZipFile 解析它,但这效率很低,并且需要您有可用存储空间,并在完成后删除文件。
因此,我发现 Apache 有一个很好的纯 Java 库(此处)来解析 Zip 文件,并且出于某种原因,它的 InputStream 解决方案(称为“ZipArchiveInputStream”)似乎比本机 ZipInputStream 类更高效。
与我们在本机框架中所拥有的相反,该库提供了更多的灵活性。例如,我可以将整个 zip 文件加载到字节数组中,并让库照常处理它,这甚至适用于我提到的有问题的 Zip 文件:
org.apache.commons.compress.archivers.zip.ZipFile(SeekableInMemoryByteChannel(byteArray)).use { zipFile ->
for (entry in zipFile.entries) {
val name = entry.name
... // use …Run Code Online (Sandbox Code Playgroud) 我正在开发一个需要获取“其他联系人”信息的应用程序,因为它们出现在Google 的“联系人”网站上的地址簿页面上:
Contacts API 已被弃用,我们需要使用 People API。
为此,我使用了 Java 库(此处),它可以更轻松地访问各种功能。
遗憾的是,根据文档(此处),从“其他联系人”查询项目列表,您最多只能获得 3 个可能的字段:
确实,在使用它时,这就是我得到的。我也有“etag”和“resourceName”字段,但就是这样。没有照片,没有封面照片,没有别的……
执行此操作的代码很短(在您设置好所有内容之后):
val otherContactsResponse =
otherContacts.list().setReadMask("emailAddresses,names,phoneNumbers")
.setPageSize(itemsCountToRequest).setPageToken(nextPageToken).execute()
val result=otherContactsResponse.otherContacts
Run Code Online (Sandbox Code Playgroud)
result.forEach { personBasic: Person ->
val test: Person? = peopleService.get(personBasic.resourceName!!)
.setPersonFields(
"addresses,ageRanges,birthdays,coverPhotos,emailAddresses,genders,metadata,names,nicknames,occupations,organizations,phoneNumbers,photos,urls")
.execute()
Log.d("AppLog", "$test")
}
Run Code Online (Sandbox Code Playgroud)
遗憾的是,没有类似的搜索“其他联系人”部分的查询(除了这里,它再次为您提供相同的 3 个字段)。
试图找出我遗漏的其他字段是否可以在其他地方使用。我甚至不知道resourceName或者etag是可靠和查询唯一的。
尽管文档只讨论了 3 个字段,但仍尝试向查询添加更多字段。失败了,当然...
有一个名为copyOtherContactToMyContactsGroup( here )的函数,它似乎会将联系人复制到主要联系人组。我想如果我使用它然后查询联系人本身(不仅仅是“其他联系人”),我可以获得所需的信息。但这既浪费时间,又会污染用户的通讯录,即使是暂时的。我必须确保在添加联系人后立即从那里删除联系人...
编辑:也试过这个,虽然它似乎有效,但我不想使用它,因为它会用用户尚未添加的联系人污染地址簿。另外,我有时会得到一个图像,它只是带有背景的人的字母。这是它的代码:
result.firstOrNull { !it.names.isNullOrEmpty()&&!it.emailAddresses.isNullOrEmpty() …Run Code Online (Sandbox Code Playgroud) 在应用程序内部,有一个使用令牌向服务器注册的 Google 登录步骤。这是通过以下依赖项完成的:
api('com.google.android.gms:play-services-auth:20.0.0')
Run Code Online (Sandbox Code Playgroud)
该应用程序会触发向用户显示最多 2 个对话框:
这对于大多数情况都适用。
如果用户已经登录并授予权限,我们可以使用上次获得的令牌,假设它没有过期。我使用以下方法检查它是否已过期:
GoogleSignIn.getLastSignedInAccount(context)?.isExpired`) .
Run Code Online (Sandbox Code Playgroud)
我发现了一个特殊的有问题的场景:
在这种情况下,我得到了一个无法使用的坏令牌。它实际上与我在撤销访问权限之前获得的令牌完全相同。它可能使用上次缓存的令牌,以避免与 Google 服务器进行不必要的通信。
在这种情况下,服务器(我所使用的 SDK)会在尝试使用该令牌时向我发送一条错误消息,指出该令牌无效(这是正确的)。
这是有问题的,在我看来就像 Google SDK 上的一个错误(我已在此处报告),因为令牌应该可以工作,因为用户已使用登录对话框重新登录,并且所有内容都已重置。
我尝试使用各种 API 函数,但它们似乎都没有告诉我令牌是否有效,或者让我为登录对话框请求一个新令牌,以防当前令牌无效。
我发现的唯一解决方法是,在一次登录对话框后,并检测到令牌有错误(通过服务器获取),我选择完全注销并重新登录:
@WorkerThread
fun logout(context: Context, googleClientId: String) {
val options =
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestServerAuthCode(googleClientId)
.requestEmail()
.build()
val signInClient = GoogleSignIn.getClient(context, options)
val lastSignedInAccount = GoogleSignIn.getLastSignedInAccount(context)
if (lastSignedInAccount != null) {
Tasks.await(signInClient.revokeAccess())
Tasks.await(signInClient.signOut())
}
}
Run Code Online (Sandbox Code Playgroud)
只有在那之后,我才能使用我准备的 Intent 登录:
@WorkerThread
fun prepareIntent(context: Context, googleClientId: String): …Run Code Online (Sandbox Code Playgroud) 假设我制作了一个名为“MySdk”的 Android 库,并将其发布到 Jitpack/Maven 上。
SDK 的用户只需添加以下依赖项即可使用它:
implementation 'com.github.my-sdk:MySdk:1.0.1'
Run Code Online (Sandbox Code Playgroud)
我想要得到的是其中的“1.0.1”部分,无论我是从 Android 库本身(对于发送到使用哪个版本的 SDK 服务器很有用),还是从应用程序执行此操作使用它(对于报告特定问题很有用,包括通过 Crashlytics)。
我找不到任何反射或 gradle 任务来实现它。
搜索一下,如果我确实在 Android 库(用作依赖项)上工作,我发现我可以通过代码自己管理版本。
有人说我可以使用库的包名称的 BuildConfig,但这意味着如果我在发布依赖项之前忘记更新代码,它将使用错误的值。使用此方法的示例:
plugins {
...
}
final def sdkVersion = "1.0.22"
android {
...
buildTypes {
release {
...
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "\""
}
debug {
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "-unreleased\""
}
}
Run Code Online (Sandbox Code Playgroud)
BuildConfig.SDK_VERSION用法只是检查(构建后)的值。
另一种可能的解决方案可能是来自 Android 库内的 gradle 任务,每当您构建使用该库的应用程序时,都会强制启动该任务。但是,我没找到怎么做(在这里找到了一些东西)
是否可以从依赖项的 Android 库(当然还有使用它的应用程序)查询依赖项版本,以便我可以在运行时使用它?
自动的东西,不需要我在发布之前更新它?
也许使用库中定义的 Gradle …
我在我工作的大型应用程序上发现了一些类/函数,它们具有不应在 UI 线程上进行的调用(例如访问存储或数据库)。
此类操作可能会导致 ANR,实际上我可以在 Play Console 上看到 ANR 的百分比。
我想改变这一点,并希望通过使用 Kotlin 协程来使代码更有秩序。
因此,目前我正在开发一个扩展类BroadcastReceiver,因此它需要onReceive在 UI 线程上一个接一个地处理回调,每个回调都必须“等待”前一个回调完成。
在 onReceive 回调内部,有一些调用应该在后台线程上完成,有些则在 UI 线程上完成。有时有些条件同时具备这两者。
含义例如:
if( someCheckOnUiThread() && someDbOperation()) {
...
}
Run Code Online (Sandbox Code Playgroud)
我对 Kotlin 协程很陌生,尽管我已经找到了如何处理这个问题,但我很确定有一种更官方的方法可以做到这一点,因为我已经阅读了其他人关于此问题的一些提示和评论(这里)。
我所做的实际上是有效的,但它看起来更像是一种解决方法:
private val mainScope = MainScope()
private val backgroundWorkDispatcher: CoroutineDispatcher =
java.util.concurrent.Executors.newFixedThreadPool(1).asCoroutineDispatcher()
Run Code Online (Sandbox Code Playgroud)
然后在回调中使用它们onReceive:
@UiThread
override fun onReceive(somcContext: Context, intent: Intent) {
val context = somcContext.applicationContext
//use goAsync just because I'm in BroadcastReceiver
val pendingAsyncResult = goAsync()
mainScope.launch …Run Code Online (Sandbox Code Playgroud) 似乎有很多关于如何在android上使用拖动视图的解决方案.
然而,它们要么太慢,要么是马车.
一个已知的解决方案是使用framelayout,视图只需在使用OnTouchListener移动时更改其layoutParams,如下所述:http://www.anddev.org/novice-tutorials-f8/android-drag-and-drop -of-normal-views-not-bitmaps-t13404.html 但是,如果视图是imageView,当到达framelayout的最右侧(或最底部)时,图像会缩小.
另一种可能的解决方案是使用画布来显示拖动的内容,如下所述:http: //www.anddev.org/basic_drag_and_drop-t3095.html 但是,我不确定如何将它用于复杂视图.
没有人知道一个很好的解决方案,一个让拖累视图的大小保持不变,不管它是在它的容器?
请帮我 .
假设我有多个图像需要将一个放在另一个上面,有些可能会出现某种动画,有些甚至可能是可拖动的.
通常占据整个屏幕的最大的一个是Z坐标的底部(让我们称之为backgroundImageView),而其余的都出现在它的顶部(并且在其他顶部).
像这样:
如果我使用FrameLayout(这似乎是最好的解决方案),backgroundImageView的大小会很合适,但是如何强制其他图层相应调整大小呢?
我想我需要以某种方式得到其他层的位置以及如何设置它们的大小.
简单的方法是确保所有图层具有完全相同的大小,但这可能会占用大量内存,并在动画或拖动视图时变得非常慢.如果某些层的内容非常小,那将是一个巨大的浪费.
我有一个主位图,我需要在其他位图上绘制.
主位图具有一些半透明像素(具有alpha通道变量值的像素),因此在其上绘制的其他位图应与其合并,而不是完全覆盖颜色.
如何设置画布相对于半透明像素在主位图上绘制位图?
注意:alpha不适用于整个位图.这是每个像素.