1656759900
Kotlin拡張機能とは何ですか?
Kotlinは、クラスから継承したり、などのデザインパターンを使用したりすることなく、クラスまたはインターフェイスを新しい機能で拡張する機能を提供します
Decorator
。これは、拡張機能と呼ばれる特別な宣言を介して行われます。
たとえば、String
主にコードでクラスを使用しました。一部のプロパティをに含めたい場合がありますが、含まれString class
ません。この場合、KotlinExtensionメソッドを使用すると可能になります。
String
文字列内のすべての文字をアルファベット順に並べ替える1つのプロパティが必要だとします。Extension
そのための関数を作ってみましょう。
fun String.sortAlphabetically() = toCharArray().apply { sort() }
拡張機能の機能とは何か、そしてそれらがどのように見えるかを理解していただければ幸いです。
1. EditText onChanged Text
テキストの編集の変更は、モバイルアプリケーションで実行する重要なタスクです。どのテキストが変更されたかを確認します。テキストの使用例の1つはEditText.onChange
、3〜4文字の文字が検索を開始したときにボタンを押さずに、検索バーで何かを検索することです。テキストに変更がある場合、関数は文字列で変更されたテキストを返します。
fun EditText.onChange(textChanged: ((String) -> Unit)) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged.invoke(s.toString())
}
})
}
2.トーストを表示
この拡張機能を使用すると、ActivitiesまたはFragmentのどこでも、またはを渡すことができる場所ならどこでもtoastを呼び出すことができますContext
。電話をかけるだけshowToast(MESSAGE_YOU_WANT_TO_SHOW)
で、デフォルトではトーストの持続時間はになりますToast.LENGTH_SHORT
。
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
message?.let {
Toast.makeText(this, it, length).show()
}
}
//Usage
showToast("Hello World👋")
3.スナックバーを表示する
Snackbar
モバイルアプリの画面下部に簡単なメッセージを表示するために使用されます。トーストの一種です。でSnackBar
、要件がある場合はアクションボタンがある場合があります。
fun View.showSnackMessage(
message: String?,
anchorView: View? = null,
backgroundColor: Int,
textColor: Int,
length: Int = Snackbar.LENGTH_SHORT
) {
message?.let {
try {
val snack = Snackbar.make(this, it, length)
snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
snack.setTextColor(ContextCompat.getColor(context, textColor))
snack.anchorView = anchorView
snack.show()
} catch (ex: Exception) {
ex.showLog()
}
}
}
4.インターネットが利用可能かどうかを確認します
ファイルのアップロードやアプリでインターネットからのあらゆる種類のデータの取得などのAPIリクエストを行う際には、インターネットが利用可能である必要があります。インターネットが利用できない場合、アプリはクラッシュします。
1つのシナリオで、Androidプロジェクトに複数のアクティビティとフラグメントがあり、インターネットが多くのポイントで利用可能であることを確認する必要がある場合、接続マネージャーを数回呼び出す必要があります。これは、プロジェクトで使用する拡張機能が1つあるためです。 。
それもあなたを助けます。
fun Context.isNetworkAvailable(): Boolean {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
} else {
try {
val activeNetworkInfo = manager.activeNetworkInfo
if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
return true
}
} catch (e: Exception) {
e.showLog()
}
}
return false
}
//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
//Perform your task
}else{
showToast("Internet is not available, Try Later")
}
インターネット接続チェック拡張機能
5.ビューの可視性を制御する
Androidで、取得したデータに応じてビューを表示または非表示にする場合。特定のビューの表示を非表示にしたい場合があります。たとえば、を使用ProgressBar
すると、一部のデータがバックエンドまたはローカルソースから取得しているときにのみプログレスバーを表示したいとします。データがフェッチされたら、プログレスバーを非表示にします。
Androidでこのアクションを実行する一般的な方法はbinding.loadingProgressBar = View.GONE
、毎回実行する必要があります。View
これで、クラスを使用して1つの拡張関数を作成するだけで、ニーズを満たすことができます。
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE
結論
それで全部です!あなたが何かを学んだことを願っています。疑問がある場合は、コメントしてください。私はあなたの問題を喜んで解決します。
ありがとう、そして乾杯!
ソース:https ://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656703320
What is a Kotlin Extension?
Kotlin provides the ability to extend a class or an interface with new functionality without having to inherit from the class or use design patterns such as
Decorator
. This is done via special declarations called extensions.
For example, you have used the String
class mostly in your code. It may happen that you want some properties to be included in the String class
, but they won’t. In this case, it is possible when using the Kotlin Extension method.
Suppose you want a String
having one property to sort all the characters in the string alphabetically. Let’s make an Extension
function for that.
fun String.sortAlphabetically() = toCharArray().apply { sort() }
I hope you get the idea of what exactly extensions' functions are and what they look like.
See more at: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656684965
Phần mở rộng Kotlin là gì?
Kotlin cung cấp khả năng mở rộng một lớp hoặc một giao diện với chức năng mới mà không cần phải kế thừa từ lớp hoặc sử dụng các mẫu thiết kế như
Decorator
. Điều này được thực hiện thông qua các khai báo đặc biệt được gọi là phần mở rộng.
Ví dụ, bạn đã sử dụng String
lớp chủ yếu trong mã của mình. Có thể xảy ra trường hợp bạn muốn một số thuộc tính được đưa vào String class
, nhưng chúng sẽ không. Trong trường hợp này, có thể thực hiện được khi sử dụng phương pháp Mở rộng Kotlin.
Giả sử bạn muốn String
có một thuộc tính để sắp xếp tất cả các ký tự trong chuỗi theo thứ tự bảng chữ cái. Hãy tạo một Extension
hàm cho điều đó.
fun String.sortAlphabetically() = toCharArray().apply { sort() }
Tôi hy vọng bạn hiểu được các chức năng chính xác của tiện ích mở rộng là gì và chúng trông như thế nào.
1. EditText onChanged Text
Thay đổi văn bản chỉnh sửa là nhiệm vụ chính cần thực hiện trong ứng dụng di động. Kiểm tra xem văn bản nào đã được thay đổi. Một trường hợp sử dụng của EditText.onChange
văn bản là tìm kiếm thứ gì đó trong thanh tìm kiếm mà không cần nhấn nút chỉ khi có 3–4 ký tự ở đó bắt đầu tìm kiếm. Khi có thay đổi trong văn bản, hàm sẽ trả về văn bản đã thay đổi trong chuỗi.
fun EditText.onChange(textChanged: ((String) -> Unit)) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged.invoke(s.toString())
}
})
}
2. Hiển thị bánh mì nướng
Với chức năng mở rộng này, bạn có thể gọi bánh mì nướng ở bất kỳ đâu trong Hoạt động hoặc Phân đoạn hoặc bất kỳ nơi nào bạn có thể vượt qua Context
. Bạn chỉ cần gọi showToast(MESSAGE_YOU_WANT_TO_SHOW)
và theo mặc định, thời lượng bánh mì nướng sẽ là Toast.LENGTH_SHORT
.
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
message?.let {
Toast.makeText(this, it, length).show()
}
}
//Usage
showToast("Hello World👋")
3. Hiển thị SnackBar
Snackbar
được sử dụng để hiển thị thông báo ngắn gọn ở cuối màn hình trên ứng dụng dành cho thiết bị di động của bạn. Đó là một loại thay thế cho bánh mì nướng. Trong SnackBar
, bạn có thể có một nút hành động nếu có yêu cầu.
fun View.showSnackMessage(
message: String?,
anchorView: View? = null,
backgroundColor: Int,
textColor: Int,
length: Int = Snackbar.LENGTH_SHORT
) {
message?.let {
try {
val snack = Snackbar.make(this, it, length)
snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
snack.setTextColor(ContextCompat.getColor(context, textColor))
snack.anchorView = anchorView
snack.show()
} catch (ex: Exception) {
ex.showLog()
}
}
}
4. Kiểm tra xem Internet có khả dụng không
Trong khi thực hiện bất kỳ yêu cầu API nào như tải lên tệp hoặc nhận bất kỳ loại dữ liệu nào từ internet trong ứng dụng của chúng tôi, chúng tôi cần đảm bảo rằng Internet phải khả dụng. Nếu Internet không khả dụng, thì ứng dụng sẽ bị treo.
Giả sử trong một tình huống có nhiều hoạt động và phân đoạn trong dự án Android của bạn và bạn cần kiểm tra Internet có khả dụng ở nhiều thời điểm hay không, sau đó bạn cần gọi trình quản lý kết nối nhiều lần, vì có một chức năng mở rộng mà tôi sử dụng trong dự án của mình. .
Nó cũng sẽ giúp bạn.
fun Context.isNetworkAvailable(): Boolean {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
} else {
try {
val activeNetworkInfo = manager.activeNetworkInfo
if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
return true
}
} catch (e: Exception) {
e.showLog()
}
}
return false
}
//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
//Perform your task
}else{
showToast("Internet is not available, Try Later")
}
Phần mở rộng kiểm tra kết nối Internet
5. Kiểm soát khả năng hiển thị của VIEWS
Trong Android, khi chúng ta muốn hiển thị hoặc ẩn chế độ xem tùy theo dữ liệu mà chúng ta nhận được. Có một số trường hợp khi chúng tôi muốn ẩn khả năng hiển thị của bất kỳ chế độ xem cụ thể nào. Ví dụ: bằng cách sử dụng, ProgressBar
chúng tôi muốn hiển thị thanh tiến trình chỉ khi một số dữ liệu đang trong quá trình lấy từ phần phụ trợ hoặc nguồn cục bộ. Khi dữ liệu được tìm nạp, chúng tôi chỉ muốn ẩn thanh tiến trình.
Thông lệ chung để thực hiện hành động này trong Android là binding.loadingProgressBar = View.GONE
nó cần phải thực hiện mọi lúc. Bây giờ, chúng ta có thể chỉ cần tạo một hàm mở rộng với View
lớp và nó sẽ đáp ứng nhu cầu của chúng ta.
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE
Sự kết luận
Đó là tất cả! Tôi hy vọng bạn đã học được một cái gì đó. Trong trường hợp có bất kỳ nghi ngờ, chỉ cần bình luận! Tôi rất vui khi giải quyết các vấn đề của bạn.
Cảm ơn và chúc mừng!
Nguồn: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656681242
什么是 Kotlin 扩展?
Kotlin 提供了使用新功能扩展类或接口的能力,而无需从类继承或使用
Decorator
. 这是通过称为扩展的特殊声明完成的。
例如,您String
主要在代码中使用了该类。您可能希望某些属性包含在 中String class
,但它们不会。在这种情况下,可以使用 Kotlin 扩展方法。
假设您想要String
一个属性来按字母顺序对字符串中的所有字符进行排序。让我们Extension
为此创建一个函数。
fun String.sortAlphabetically() = toCharArray().apply { sort() }
我希望您了解扩展的功能到底是什么以及它们的外观。
1.EditText onChanged 文本
编辑文本更改是在移动应用程序中执行的一项关键任务。检查是否更改了哪些文本。文本的一个用例EditText.onChange
是在搜索栏中搜索某些内容,而无需在 3-4 个字符开始搜索时按下按钮。当文本发生更改时,该函数将返回字符串中更改的文本。
fun EditText.onChange(textChanged: ((String) -> Unit)) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged.invoke(s.toString())
}
})
}
2. 展示吐司
使用此扩展功能,您可以在活动或片段中的任何位置或您可以传递Context
. 您只需拨打电话showToast(MESSAGE_YOU_WANT_TO_SHOW)
,默认情况下,吐司持续时间为Toast.LENGTH_SHORT
.
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
message?.let {
Toast.makeText(this, it, length).show()
}
}
//Usage
showToast("Hello World👋")
3. 显示 SnackBar
Snackbar
用于在您的移动应用程序屏幕底部显示简短消息。它是吐司的一种替代品。在SnackBar
中,如果有需要,您可能会有一个操作按钮。
fun View.showSnackMessage(
message: String?,
anchorView: View? = null,
backgroundColor: Int,
textColor: Int,
length: Int = Snackbar.LENGTH_SHORT
) {
message?.let {
try {
val snack = Snackbar.make(this, it, length)
snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
snack.setTextColor(ContextCompat.getColor(context, textColor))
snack.anchorView = anchorView
snack.show()
} catch (ex: Exception) {
ex.showLog()
}
}
}
4. 检查互联网是否可用
在我们的应用程序中发出任何 API 请求(例如上传文件或从 Internet 获取任何类型的数据)时,我们需要确保 Internet 必须可用。如果互联网不可用,则应用程序将崩溃。
假设在一种情况下,您的 Android 项目中有多个活动和片段,并且您需要检查互联网在很多时候是否可用,然后您需要多次调用连接管理器,因为我在项目中使用了一个扩展功能.
它也会帮助你。
fun Context.isNetworkAvailable(): Boolean {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
} else {
try {
val activeNetworkInfo = manager.activeNetworkInfo
if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
return true
}
} catch (e: Exception) {
e.showLog()
}
}
return false
}
//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
//Perform your task
}else{
showToast("Internet is not available, Try Later")
}
互联网连接检查扩展
5. 控制 VIEWS 的可见性
在Android中,当我们想根据我们得到的数据显示或隐藏视图时。在某些情况下,我们想要隐藏任何特定视图的可见性。例如,使用ProgressBar
我们希望仅在某些数据正在从后端或本地源获取的过程中显示进度条。获取数据后,我们只想让进度条不可见。
在 Android 中执行此操作的一般做法是binding.loadingProgressBar = View.GONE
,每次都需要执行。现在,我们可以简单地使用该类创建一个扩展函数View
,它将满足我们的需求。
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE
结论
就这样!我希望你学到了一些东西。如有任何疑问,请发表评论!我很乐意解决您的问题。
谢谢,欢呼!
来源:https ://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656681138
O que é uma extensão Kotlin?
Kotlin fornece a capacidade de estender uma classe ou uma interface com novas funcionalidades sem ter que herdar da classe ou usar padrões de design como
Decorator
. Isso é feito por meio de declarações especiais chamadas extensões.
Por exemplo, você usou a String
classe principalmente em seu código. Pode acontecer que você queira que algumas propriedades sejam incluídas no String class
, mas elas não serão. Nesse caso, é possível ao usar o método Kotlin Extension.
Suponha que você queira String
ter uma propriedade para classificar todos os caracteres na string em ordem alfabética. Vamos fazer uma Extension
função para isso.
fun String.sortAlphabetically() = toCharArray().apply { sort() }
Espero que você tenha uma ideia de quais são exatamente as funções das extensões e como elas se parecem.
1. Editar texto em texto alterado
Uma alteração de texto de edição é uma tarefa importante a ser executada no aplicativo móvel. Verificando se o texto foi alterado. Um caso de uso de EditText.onChange
texto é pesquisar algo na barra de pesquisa, sem pressionar um botão apenas quando 3 a 4 caracteres estiverem lá, comece a pesquisar. Quando houver uma alteração no texto, a função retornará o texto alterado na string.
fun EditText.onChange(textChanged: ((String) -> Unit)) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged.invoke(s.toString())
}
})
}
2. Mostrar brinde
Com esta função de extensão, você pode chamar o toast em qualquer lugar em Atividades ou Fragmento ou em qualquer lugar em que possa passar o arquivo Context
. Você só precisa chamar showToast(MESSAGE_YOU_WANT_TO_SHOW)
e, por padrão, a duração do brinde será Toast.LENGTH_SHORT
.
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
message?.let {
Toast.makeText(this, it, length).show()
}
}
//Usage
showToast("Hello World👋")
3. Mostrar SnackBar
Snackbar
é usado para mostrar a breve mensagem na parte inferior da tela em seu aplicativo móvel. É uma espécie de substituto para torradas. Em SnackBar
, você pode ter um botão de ação se houver um requisito.
fun View.showSnackMessage(
message: String?,
anchorView: View? = null,
backgroundColor: Int,
textColor: Int,
length: Int = Snackbar.LENGTH_SHORT
) {
message?.let {
try {
val snack = Snackbar.make(this, it, length)
snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
snack.setTextColor(ContextCompat.getColor(context, textColor))
snack.anchorView = anchorView
snack.show()
} catch (ex: Exception) {
ex.showLog()
}
}
}
4. Verifique se a Internet está disponível
Ao fazer qualquer solicitação de API, como fazer upload de um arquivo ou obter qualquer tipo de dados da Internet em nosso aplicativo, precisamos garantir que a Internet esteja disponível. Se a internet não estiver disponível, o aplicativo travará.
Suponha que em um cenário existem várias atividades e fragmentos em seu projeto Android e você precisa verificar se a internet está disponível em muitos pontos, então você precisa chamar o gerenciador de conectividade várias vezes, para isso existe uma função de extensão que eu uso no meu projeto .
Vai te ajudar também.
fun Context.isNetworkAvailable(): Boolean {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
} else {
try {
val activeNetworkInfo = manager.activeNetworkInfo
if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
return true
}
} catch (e: Exception) {
e.showLog()
}
}
return false
}
//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
//Perform your task
}else{
showToast("Internet is not available, Try Later")
}
Extensão de verificação de conectividade com a Internet
5. Controlar a visibilidade das VISUALIZAÇÕES
No Android, quando queremos mostrar ou ocultar a visualização de acordo com os dados que obtemos. Existem algumas circunstâncias em que queremos ocultar a visibilidade de qualquer visualização específica. Por exemplo, usando ProgressBar
queremos mostrar a barra de progresso apenas quando alguns dados estão em processo de obtenção do back-end ou fonte local. Uma vez que os dados são buscados, queremos apenas tornar a barra de progresso invisível.
A prática geral para executar essa ação no Android é binding.loadingProgressBar = View.GONE
, ela precisa ser executada sempre. Agora, podemos simplesmente criar uma função de extensão com a View
classe e ela atenderá às nossas necessidades.
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE
Conclusão
Isso é tudo! Espero que você tenha aprendido alguma coisa. Qualquer dúvida é só comentar! Ficarei feliz em resolver seus problemas.
Obrigado, e aplausos!
Fonte: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656677412
¿Qué es una extensión de Kotlin?
Kotlin brinda la capacidad de extender una clase o una interfaz con nueva funcionalidad sin tener que heredar de la clase o usar patrones de diseño como
Decorator
. Esto se hace a través de declaraciones especiales llamadas extensiones.
Por ejemplo, ha usado la String
clase principalmente en su código. Puede suceder que desee que se incluyan algunas propiedades en el String class
, pero no lo harán. En este caso, es posible al usar el método Kotlin Extension.
Suponga que desea String
tener una propiedad para ordenar alfabéticamente todos los caracteres de la cadena. Hagamos una Extension
función para eso.
fun String.sortAlphabetically() = toCharArray().apply { sort() }
Espero que tengas una idea de cuáles son exactamente las funciones de las extensiones y cómo se ven.
1. EditText onChangeText
Un cambio de texto de edición es una tarea clave para realizar en la aplicación móvil. Comprobando si se ha cambiado qué texto. Un caso de uso de EditText.onChange
texto es buscar algo en la barra de búsqueda, sin presionar un botón, justo cuando hay 3 o 4 caracteres que comienzan a buscar. Cuando haya un cambio en el texto, la función devolverá el texto modificado en la cadena.
fun EditText.onChange(textChanged: ((String) -> Unit)) { this.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { textChanged.invoke(s.toString()) } })}
2. Mostrar tostadas
Con esta función de extensión, puede llamar al brindis en cualquier lugar, ya sea en Actividades o Fragmento, o en cualquier lugar donde pueda pasar el archivo Context
. Solo necesita llamar showToast(MESSAGE_YOU_WANT_TO_SHOW)
y, de forma predeterminada, la duración del brindis será Toast.LENGTH_SHORT
.
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) { message?.let { Toast.makeText(this, it, length).show() }}//UsageshowToast("Hello World👋")
3. Mostrar SnackBar
Snackbar
se utiliza para mostrar el breve mensaje en la parte inferior de la pantalla de su aplicación móvil. Es una especie de sustituto de las tostadas. En SnackBar
, puede tener un botón de acción si hay un requisito.
fun View.showSnackMessage( message: String?, anchorView: View? = null, backgroundColor: Int, textColor: Int, length: Int = Snackbar.LENGTH_SHORT) { message?.let { try { val snack = Snackbar.make(this, it, length) snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor)) snack.setTextColor(ContextCompat.getColor(context, textColor)) snack.anchorView = anchorView snack.show() } catch (ex: Exception) { ex.showLog() } }}
4. Compruebe si Internet está disponible
Al realizar cualquier solicitud de API, como cargar un archivo u obtener cualquier tipo de datos de Internet en nuestra aplicación, debemos asegurarnos de que Internet debe estar disponible. Si Internet no está disponible, la aplicación se bloqueará.
Supongamos que en un escenario hay múltiples actividades y fragmentos en su proyecto de Android y necesita verificar que Internet esté disponible en muchos puntos, luego debe llamar al administrador de conectividad varias veces, para eso hay una función de extensión que uso en mi proyecto .
También te ayudará.
fun Context.isNetworkAvailable(): Boolean { val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val capabilities = manager.getNetworkCapabilities(manager.activeNetwork) if (capabilities != null) { if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return true } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { return true } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { return true } } } else { try { val activeNetworkInfo = manager.activeNetworkInfo if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { return true } } catch (e: Exception) { e.showLog() } } return false}//Usage//For example, In the MainActivity, how to call this?if(isNetworkAvailable()){ //Perform your task}else{ showToast("Internet is not available, Try Later")}
Extensión de verificación de conectividad a Internet
5. Controlar la visibilidad de las VISTAS
En Android, cuando queremos mostrar u ocultar la vista según los datos que obtengamos. Hay algunas circunstancias en las que queremos ocultar la visibilidad de una vista específica. Por ejemplo, ProgressBar
queremos mostrar la barra de progreso solo cuando algunos datos están en proceso de obtenerse del backend o fuente local. Una vez que se obtienen los datos, solo queremos que la barra de progreso sea invisible.
La práctica general para realizar esta acción en Android es binding.loadingProgressBar = View.GONE
que debe realizarse cada vez. Ahora, simplemente podemos crear una función de extensión con la View
clase y satisfará nuestras necesidades.
fun View.show() { visibility = View.VISIBLE}fun View.hide() { visibility = View.GONE}fun View.invisible() { visibility = View.INVISIBLE}//Removing visibility of ProgressBar//Using Extensionbinding.loadingPb.hide()//Without Extensionbinding.loadingPb.visibility = View.GONE
Conclusión
¡Eso es todo! Espero que hayas aprendido algo. Ante cualquier duda, ¡comenta! Estaré feliz de resolver sus problemas.
¡Gracias y ánimo!
Fuente: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656677289
Qu'est-ce qu'une extension Kotlin ?
Kotlin offre la possibilité d'étendre une classe ou une interface avec de nouvelles fonctionnalités sans avoir à hériter de la classe ou à utiliser des modèles de conception tels que
Decorator
. Cela se fait via des déclarations spéciales appelées extensions.
Par exemple, vous avez utilisé la String
classe principalement dans votre code. Il peut arriver que vous vouliez que certaines propriétés soient incluses dans le String class
, mais elles ne le seront pas. Dans ce cas, c'est possible en utilisant la méthode d'extension Kotlin.
Supposons que vous souhaitiez String
avoir une propriété pour trier tous les caractères de la chaîne par ordre alphabétique. Créons une Extension
fonction pour cela.
fun String.sortAlphabetically() = toCharArray().apply { sort() }
J'espère que vous avez une idée de ce que sont exactement les fonctions des extensions et à quoi elles ressemblent.
1. EditText onChanged Text
Un changement de texte d'édition est une tâche clé à effectuer dans l'application mobile. Vérifier si quel texte a été modifié. Un cas d'utilisation du EditText.onChange
texte consiste à rechercher quelque chose dans la barre de recherche, sans appuyer sur un bouton juste au moment où 3 à 4 caractères sont là pour commencer la recherche. Lorsqu'il y aura un changement dans le texte, la fonction renverra le texte modifié dans la chaîne.
fun EditText.onChange(textChanged: ((String) -> Unit)) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged.invoke(s.toString())
}
})
}
2. Afficher le toast
Avec cette fonction d'extension, vous pouvez appeler toast n'importe où dans Activités ou Fragment ou n'importe où vous pouvez passer le Context
. Il vous suffit d'appeler showToast(MESSAGE_YOU_WANT_TO_SHOW)
et par défaut, la durée du toast sera de Toast.LENGTH_SHORT
.
fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
message?.let {
Toast.makeText(this, it, length).show()
}
}
//Usage
showToast("Hello World👋")
3. Afficher SnackBar
Snackbar
est utilisé pour afficher le bref message en bas de l'écran de votre application mobile. C'est une sorte de substitut aux toasts. Dans SnackBar
, vous pouvez avoir un bouton d'action s'il y a une exigence.
fun View.showSnackMessage(
message: String?,
anchorView: View? = null,
backgroundColor: Int,
textColor: Int,
length: Int = Snackbar.LENGTH_SHORT
) {
message?.let {
try {
val snack = Snackbar.make(this, it, length)
snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
snack.setTextColor(ContextCompat.getColor(context, textColor))
snack.anchorView = anchorView
snack.show()
} catch (ex: Exception) {
ex.showLog()
}
}
}
4. Vérifiez si Internet est disponible
Lors de toute demande d'API, comme le téléchargement d'un fichier ou l'obtention de tout type de données d'Internet dans notre application, nous devons nous assurer qu'Internet doit être disponible. Si Internet n'est pas disponible, l'application plantera.
Supposons que dans un scénario, il y ait plusieurs activités et fragments dans votre projet Android et que vous deviez vérifier qu'Internet est disponible à de nombreux points, puis vous devez appeler le gestionnaire de connectivité plusieurs fois, pour cela il y a une fonction d'extension que j'utilise dans mon projet .
Cela vous aidera aussi.
fun Context.isNetworkAvailable(): Boolean {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
} else {
try {
val activeNetworkInfo = manager.activeNetworkInfo
if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
return true
}
} catch (e: Exception) {
e.showLog()
}
}
return false
}
//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
//Perform your task
}else{
showToast("Internet is not available, Try Later")
}
Extension de vérification de la connectivité Internet
5. Contrôler la visibilité des VUES
Dans Android, lorsque nous voulons afficher ou masquer la vue en fonction des données que nous obtenons. Dans certaines circonstances, nous souhaitons masquer la visibilité d'une vue spécifique. Par exemple, en utilisant, ProgressBar
nous souhaitons afficher la barre de progression uniquement lorsque certaines données sont en cours d'obtention depuis le backend ou la source locale. Une fois les données récupérées, nous voulons simplement rendre la barre de progression invisible.
La pratique générale pour effectuer cette action dans Android est binding.loadingProgressBar = View.GONE
qu'elle doit être exécutée à chaque fois. Maintenant, nous pouvons simplement créer une fonction d'extension avec la View
classe, et elle répondra à nos besoins.
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE
Conclusion
C'est tout! J'espère que vous avez appris quelque chose. En cas de doute, il suffit de commenter ! Je serai heureux de résoudre vos problèmes.
Merci et bravo !
Source : https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8
1656203400
Moshi is a modern JSON library for Android, Java and Kotlin. It makes it easy to parse JSON into Java and Kotlin classes:
Note: The Kotlin examples of this README assume use of either Kotlin code gen or KotlinJsonAdapterFactory
for reflection. Plain Java-based reflection is unsupported on Kotlin classes.
Java
String json = ...;
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);
BlackjackHand blackjackHand = jsonAdapter.fromJson(json);
System.out.println(blackjackHand);
Kotlin
val json: String = ...
val moshi: Moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>()
val blackjackHand = jsonAdapter.fromJson(json)
println(blackjackHand)
And it can just as easily serialize Java or Kotlin objects as JSON:
Java
BlackjackHand blackjackHand = new BlackjackHand(
new Card('6', SPADES),
Arrays.asList(new Card('4', CLUBS), new Card('A', HEARTS)));
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);
String json = jsonAdapter.toJson(blackjackHand);
System.out.println(json);
Kotlin
val blackjackHand = BlackjackHand(
Card('6', SPADES),
listOf(Card('4', CLUBS), Card('A', HEARTS))
)
val moshi: Moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>()
val json: String = jsonAdapter.toJson(blackjackHand)
println(json)
Moshi has built-in support for reading and writing Java’s core data types:
It supports your model classes by writing them out field-by-field. In the example above Moshi uses these classes:
Java
class BlackjackHand {
public final Card hidden_card;
public final List<Card> visible_cards;
...
}
class Card {
public final char rank;
public final Suit suit;
...
}
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
}
Kotlin
class BlackjackHand(
val hidden_card: Card,
val visible_cards: List<Card>,
...
)
class Card(
val rank: Char,
val suit: Suit
...
)
enum class Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
}
to read and write this JSON:
{
"hidden_card": {
"rank": "6",
"suit": "SPADES"
},
"visible_cards": [
{
"rank": "4",
"suit": "CLUBS"
},
{
"rank": "A",
"suit": "HEARTS"
}
]
}
The Javadoc catalogs the complete Moshi API, which we explore below.
With Moshi, it’s particularly easy to customize how values are converted to and from JSON. A type adapter is any class that has methods annotated @ToJson
and @FromJson
.
For example, Moshi’s default encoding of a playing card is verbose: the JSON defines the rank and suit in separate fields: {"rank":"A","suit":"HEARTS"}
. With a type adapter, we can change the encoding to something more compact: "4H"
for the four of hearts or "JD"
for the jack of diamonds:
Java
class CardAdapter {
@ToJson String toJson(Card card) {
return card.rank + card.suit.name().substring(0, 1);
}
@FromJson Card fromJson(String card) {
if (card.length() != 2) throw new JsonDataException("Unknown card: " + card);
char rank = card.charAt(0);
switch (card.charAt(1)) {
case 'C': return new Card(rank, Suit.CLUBS);
case 'D': return new Card(rank, Suit.DIAMONDS);
case 'H': return new Card(rank, Suit.HEARTS);
case 'S': return new Card(rank, Suit.SPADES);
default: throw new JsonDataException("unknown suit: " + card);
}
}
}
Kotlin
class CardAdapter {
@ToJson fun toJson(card: Card): String {
return card.rank + card.suit.name.substring(0, 1)
}
@FromJson fun fromJson(card: String): Card {
if (card.length != 2) throw JsonDataException("Unknown card: $card")
val rank = card[0]
return when (card[1]) {
'C' -> Card(rank, Suit.CLUBS)
'D' -> Card(rank, Suit.DIAMONDS)
'H' -> Card(rank, Suit.HEARTS)
'S' -> Card(rank, Suit.SPADES)
else -> throw JsonDataException("unknown suit: $card")
}
}
}
Register the type adapter with the Moshi.Builder
and we’re good to go.
Java
Moshi moshi = new Moshi.Builder()
.add(new CardAdapter())
.build();
Kotlin
val moshi = Moshi.Builder()
.add(CardAdapter())
.build()
Voilà:
{
"hidden_card": "6S",
"visible_cards": [
"4C",
"AH"
]
}
Note that the method annotated with @FromJson
does not need to take a String as an argument. Rather it can take input of any type and Moshi will first parse the JSON to an object of that type and then use the @FromJson
method to produce the desired final value. Conversely, the method annotated with @ToJson
does not have to produce a String.
Assume, for example, that we have to parse a JSON in which the date and time of an event are represented as two separate strings.
{
"title": "Blackjack tournament",
"begin_date": "20151010",
"begin_time": "17:04"
}
We would like to combine these two fields into one string to facilitate the date parsing at a later point. Also, we would like to have all variable names in CamelCase. Therefore, the Event
class we want Moshi to produce like this:
Java
class Event {
String title;
String beginDateAndTime;
}
Kotlin
class Event(
val title: String,
val beginDateAndTime: String
)
Instead of manually parsing the JSON line per line (which we could also do) we can have Moshi do the transformation automatically. We simply define another class EventJson
that directly corresponds to the JSON structure:
Java
class EventJson {
String title;
String begin_date;
String begin_time;
}
Kotlin
class EventJson(
val title: String,
val begin_date: String,
val begin_time: String
)
And another class with the appropriate @FromJson
and @ToJson
methods that are telling Moshi how to convert an EventJson
to an Event
and back. Now, whenever we are asking Moshi to parse a JSON to an Event
it will first parse it to an EventJson
as an intermediate step. Conversely, to serialize an Event
Moshi will first create an EventJson
object and then serialize that object as usual.
Java
class EventJsonAdapter {
@FromJson Event eventFromJson(EventJson eventJson) {
Event event = new Event();
event.title = eventJson.title;
event.beginDateAndTime = eventJson.begin_date + " " + eventJson.begin_time;
return event;
}
@ToJson EventJson eventToJson(Event event) {
EventJson json = new EventJson();
json.title = event.title;
json.begin_date = event.beginDateAndTime.substring(0, 8);
json.begin_time = event.beginDateAndTime.substring(9, 14);
return json;
}
}
Kotlin
class EventJsonAdapter {
@FromJson
fun eventFromJson(eventJson: EventJson): Event {
return Event(
title = eventJson.title,
beginDateAndTime = "${eventJson.begin_date} ${eventJson.begin_time}"
)
}
@ToJson
fun eventToJson(event: Event): EventJson {
return EventJson(
title = event.title,
begin_date = event.beginDateAndTime.substring(0, 8),
begin_time = event.beginDateAndTime.substring(9, 14),
)
}
}
Again we register the adapter with Moshi.
Java
Moshi moshi = new Moshi.Builder()
.add(new EventJsonAdapter())
.build();
Kotlin
val moshi = Moshi.Builder()
.add(EventJsonAdapter())
.build()
We can now use Moshi to parse the JSON directly to an Event
.
Java
JsonAdapter<Event> jsonAdapter = moshi.adapter(Event.class);
Event event = jsonAdapter.fromJson(json);
Kotlin
val jsonAdapter = moshi.adapter<Event>()
val event = jsonAdapter.fromJson(json)
Moshi provides a number of convenience methods for JsonAdapter
objects:
nullSafe()
nonNull()
lenient()
failOnUnknown()
indent()
serializeNulls()
These factory methods wrap an existing JsonAdapter
into additional functionality. For example, if you have an adapter that doesn't support nullable values, you can use nullSafe()
to make it null safe:
Java
String dateJson = "\"2018-11-26T11:04:19.342668Z\"";
String nullDateJson = "null";
// Hypothetical IsoDateDapter, doesn't support null by default
JsonAdapter<Date> adapter = new IsoDateDapter();
Date date = adapter.fromJson(dateJson);
System.out.println(date); // Mon Nov 26 12:04:19 CET 2018
Date nullDate = adapter.fromJson(nullDateJson);
// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $
Date nullDate = adapter.nullSafe().fromJson(nullDateJson);
System.out.println(nullDate); // null
Kotlin
val dateJson = "\"2018-11-26T11:04:19.342668Z\""
val nullDateJson = "null"
// Hypothetical IsoDateDapter, doesn't support null by default
val adapter: JsonAdapter<Date> = IsoDateDapter()
val date = adapter.fromJson(dateJson)
println(date) // Mon Nov 26 12:04:19 CET 2018
val nullDate = adapter.fromJson(nullDateJson)
// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $
val nullDate = adapter.nullSafe().fromJson(nullDateJson)
println(nullDate) // null
In contrast to nullSafe()
there is nonNull()
to make an adapter refuse null values. Refer to the Moshi JavaDoc for details on the various methods.
Say we have a JSON string of this structure:
[
{
"rank": "4",
"suit": "CLUBS"
},
{
"rank": "A",
"suit": "HEARTS"
}
]
We can now use Moshi to parse the JSON string into a List<Card>
.
Java
String cardsJsonResponse = ...;
Type type = Types.newParameterizedType(List.class, Card.class);
JsonAdapter<List<Card>> adapter = moshi.adapter(type);
List<Card> cards = adapter.fromJson(cardsJsonResponse);
Kotlin
val cardsJsonResponse: String = ...
// We can just use a reified extension!
val adapter = moshi.adapter<List<Card>>()
val cards: List<Card> = adapter.fromJson(cardsJsonResponse)
Automatic databinding almost feels like magic. But unlike the black magic that typically accompanies reflection, Moshi is designed to help you out when things go wrong.
JsonDataException: Expected one of [CLUBS, DIAMONDS, HEARTS, SPADES] but was ANCHOR at path $.visible_cards[2].suit
at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:188)
at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:180)
...
Moshi always throws a standard java.io.IOException
if there is an error reading the JSON document, or if it is malformed. It throws a JsonDataException
if the JSON document is well-formed, but doesn’t match the expected format.
Moshi uses Okio for simple and powerful I/O. It’s a fine complement to OkHttp, which can share buffer segments for maximum efficiency.
Moshi uses the same streaming and binding mechanisms as Gson. If you’re a Gson user you’ll find Moshi works similarly. If you try Moshi and don’t love it, you can even migrate to Gson without much violence!
But the two libraries have a few important differences:
java.*
, javax.*
, and android.*
) without a user-provided type adapter. This is intended to prevent you from accidentally locking yourself to a specific JDK or Android release.visibleCards
and using a policy class to convert that to visible_cards
, Moshi wants you to just name the field visible_cards
as it appears in the JSON.JsonElement
model. Instead it just uses built-in types like List
and Map
.=
as \u003d
by default so that it can be safely encoded in HTML without additional escaping. Moshi encodes it naturally (as =
) and assumes that the HTML encoder – if there is one – will do its job.Moshi works best when your JSON objects and Java or Kotlin classes have the same structure. But when they don't, Moshi has annotations to customize data binding.
Use @Json
to specify how Java fields or Kotlin properties map to JSON names. This is necessary when the JSON name contains spaces or other characters that aren’t permitted in Java field or Kotlin property names. For example, this JSON has a field name containing a space:
{
"username": "jesse",
"lucky number": 32
}
With @Json
its corresponding Java or Kotlin class is easy:
Java
class Player {
String username;
@Json(name = "lucky number") int luckyNumber;
...
}
Kotlin
class Player {
val username: String
@Json(name = "lucky number") val luckyNumber: Int
...
}
Because JSON field names are always defined with their Java or Kotlin fields, Moshi makes it easy to find fields when navigating between Java or Koltin and JSON.
Use @JsonQualifier
to customize how a type is encoded for some fields without changing its encoding everywhere. This works similarly to the qualifier annotations in dependency injection tools like Dagger and Guice.
Here’s a JSON message with two integers and a color:
{
"width": 1024,
"height": 768,
"color": "#ff0000"
}
By convention, Android programs also use int
for colors:
Java
class Rectangle {
int width;
int height;
int color;
}
Kotlin
class Rectangle(
val width: Int,
val height: Int,
val color: Int
)
But if we encoded the above Java or Kotlin class as JSON, the color isn't encoded properly!
{
"width": 1024,
"height": 768,
"color": 16711680
}
The fix is to define a qualifier annotation, itself annotated @JsonQualifier
:
Java
@Retention(RUNTIME)
@JsonQualifier
public @interface HexColor {
}
Kotlin
@Retention(RUNTIME)
@JsonQualifier
annotation class HexColor
Next apply this @HexColor
annotation to the appropriate field:
Java
class Rectangle {
int width;
int height;
@HexColor int color;
}
Kotlin
class Rectangle(
val width: Int,
val height: Int,
@HexColor val color: Int
)
And finally define a type adapter to handle it:
Java
/** Converts strings like #ff0000 to the corresponding color ints. */
class ColorAdapter {
@ToJson String toJson(@HexColor int rgb) {
return String.format("#%06x", rgb);
}
@FromJson @HexColor int fromJson(String rgb) {
return Integer.parseInt(rgb.substring(1), 16);
}
}
Kotlin
/** Converts strings like #ff0000 to the corresponding color ints. */
class ColorAdapter {
@ToJson fun toJson(@HexColor rgb: Int): String {
return "#%06x".format(rgb)
}
@FromJson @HexColor fun fromJson(rgb: String): Int {
return rgb.substring(1).toInt(16)
}
}
Use @JsonQualifier
when you need different JSON encodings for the same type. Most programs shouldn’t need this @JsonQualifier
, but it’s very handy for those that do.
Some models declare fields that shouldn’t be included in JSON. For example, suppose our blackjack hand has a total
field with the sum of the cards:
Java
public final class BlackjackHand {
private int total;
...
}
Kotlin
class BlackjackHand(
private val total: Int,
...
)
By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding JSON. Prevent a field from being included by annotating them with @Json(ignore = true)
.
Java
public final class BlackjackHand {
@Json(ignore = true)
private int total;
...
}
Kotlin
class BlackjackHand(...) {
@Json(ignore = true)
var total: Int = 0
...
}
These fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead, it will get a default value. In Kotlin, these fields must have a default value if they are in the primary constructor.
Note that you can also use Java’s transient
keyword or Kotlin's @Transient
annotation on these fields for the same effect.
When reading JSON that is missing a field, Moshi relies on the Java or Kotlin or Android runtime to assign the field’s value. Which value it uses depends on whether the class has a no-arguments constructor.
If the class has a no-arguments constructor, Moshi will call that constructor and whatever value it assigns will be used. For example, because this class has a no-arguments constructor the total
field is initialized to -1
.
Note: This section only applies to Java reflections.
public final class BlackjackHand {
private int total = -1;
...
private BlackjackHand() {
}
public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
...
}
}
If the class doesn’t have a no-arguments constructor, Moshi can’t assign the field’s default value, even if it’s specified in the field declaration. Instead, the field’s default is always 0
for numbers, false
for booleans, and null
for references. In this example, the default value of total
is 0
!
public final class BlackjackHand {
private int total = -1;
...
public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
...
}
}
This is surprising and is a potential source of bugs! For this reason consider defining a no-arguments constructor in classes that you use with Moshi, using @SuppressWarnings("unused")
to prevent it from being inadvertently deleted later:
public final class BlackjackHand {
private int total = -1;
...
@SuppressWarnings("unused") // Moshi uses this!
private BlackjackHand() {
}
public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
...
}
}
In some situations Moshi's default Java-to-JSON conversion isn't sufficient. You can compose adapters to build upon the standard conversion.
In this example, we turn serialize nulls, then delegate to the built-in adapter:
Java
class TournamentWithNullsAdapter {
@ToJson void toJson(JsonWriter writer, Tournament tournament,
JsonAdapter<Tournament> delegate) throws IOException {
boolean wasSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(true);
try {
delegate.toJson(writer, tournament);
} finally {
writer.setLenient(wasSerializeNulls);
}
}
}
Kotlin
class TournamentWithNullsAdapter {
@ToJson fun toJson(writer: JsonWriter, tournament: Tournament?,
delegate: JsonAdapter<Tournament?>) {
val wasSerializeNulls: Boolean = writer.getSerializeNulls()
writer.setSerializeNulls(true)
try {
delegate.toJson(writer, tournament)
} finally {
writer.setLenient(wasSerializeNulls)
}
}
}
When we use this to serialize a tournament, nulls are written! But nulls elsewhere in our JSON document are skipped as usual.
Moshi has a powerful composition system in its JsonAdapter.Factory
interface. We can hook in to the encoding and decoding process for any type, even without knowing about the types beforehand. In this example, we customize types annotated @AlwaysSerializeNulls
, which an annotation we create, not built-in to Moshi:
Java
@Target(TYPE)
@Retention(RUNTIME)
public @interface AlwaysSerializeNulls {}
Kotlin
@Target(TYPE)
@Retention(RUNTIME)
annotation class AlwaysSerializeNulls
Java
@AlwaysSerializeNulls
static class Car {
String make;
String model;
String color;
}
Kotlin
@AlwaysSerializeNulls
class Car(
val make: String?,
val model: String?,
val color: String?
)
Each JsonAdapter.Factory
interface is invoked by Moshi
when it needs to build an adapter for a user's type. The factory either returns an adapter to use, or null if it doesn't apply to the requested type. In our case we match all classes that have our annotation.
Java
static class AlwaysSerializeNullsFactory implements JsonAdapter.Factory {
@Override public JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type);
if (!rawType.isAnnotationPresent(AlwaysSerializeNulls.class)) {
return null;
}
JsonAdapter<Object> delegate = moshi.nextAdapter(this, type, annotations);
return delegate.serializeNulls();
}
}
Kotlin
class AlwaysSerializeNullsFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
val rawType: Class<*> = type.rawType
if (!rawType.isAnnotationPresent(AlwaysSerializeNulls::class.java)) {
return null
}
val delegate: JsonAdapter<Any> = moshi.nextAdapter(this, type, annotations)
return delegate.serializeNulls()
}
}
After determining that it applies, the factory looks up Moshi's built-in adapter by calling Moshi.nextAdapter()
. This is key to the composition mechanism: adapters delegate to each other! The composition in this example is simple: it applies the serializeNulls()
transform on the delegate.
Composing adapters can be very sophisticated:
An adapter could transform the input object before it is JSON-encoded. A string could be trimmed or truncated; a value object could be simplified or normalized.
An adapter could repair the output object after it is JSON-decoded. It could fill-in missing data or discard unwanted data.
The JSON could be given extra structure, such as wrapping values in objects or arrays.
Moshi is itself built on the pattern of repeatedly composing adapters. For example, Moshi's built-in adapter for List<T>
delegates to the adapter of T
, and calls it repeatedly.
Moshi's composition mechanism tries to find the best adapter for each type. It starts with the first adapter or factory registered with Moshi.Builder.add()
, and proceeds until it finds an adapter for the target type.
If a type can be matched multiple adapters, the earliest one wins.
To register an adapter at the end of the list, use Moshi.Builder.addLast()
instead. This is most useful when registering general-purpose adapters, such as the KotlinJsonAdapterFactory
below.
Moshi is a great JSON library for Kotlin. It understands Kotlin’s non-nullable types and default parameter values. When you use Kotlin with Moshi you may use reflection, codegen, or both.
The reflection adapter uses Kotlin’s reflection library to convert your Kotlin classes to and from JSON. Enable it by adding the KotlinJsonAdapterFactory
to your Moshi.Builder
:
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
Moshi’s adapters are ordered by precedence, so you should use addLast()
with KotlinJsonAdapterFactory
, and add()
with your custom adapters.
The reflection adapter requires the following additional dependency:
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi-kotlin</artifactId>
<version>1.12.0</version>
</dependency>
implementation("com.squareup.moshi:moshi-kotlin:1.13.0")
Note that the reflection adapter transitively depends on the kotlin-reflect
library which is a 2.5 MiB .jar file.
Moshi’s Kotlin codegen support can be used as an annotation processor (via kapt) or Kotlin SymbolProcessor (KSP). It generates a small and fast adapter for each of your Kotlin classes at compile-time. Enable it by annotating each class that you want to encode as JSON:
@JsonClass(generateAdapter = true)
data class BlackjackHand(
val hidden_card: Card,
val visible_cards: List<Card>
)
The codegen adapter requires that your Kotlin types and their properties be either internal
or public
(this is Kotlin’s default visibility).
Kotlin codegen has no additional runtime dependency. You’ll need to enable kapt or KSP and then add the following to your build to enable the annotation processor:
KSP
plugins {
id("com.google.devtools.ksp").version("1.6.10-1.0.4") // Or latest version of KSP
}
dependencies {
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")
}
Kapt
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi-kotlin-codegen</artifactId>
<version>1.12.0</version>
<scope>provided</scope>
</dependency>
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")
If your Kotlin class has a superclass, it must also be a Kotlin class. Neither reflection or codegen support Kotlin types with Java supertypes or Java types with Kotlin supertypes. If you need to convert such classes to JSON you must create a custom type adapter.
The JSON encoding of Kotlin types is the same whether using reflection or codegen. Prefer codegen for better performance and to avoid the kotlin-reflect
dependency; prefer reflection to convert both private and protected properties. If you have configured both, generated adapters will be used on types that are annotated @JsonClass(generateAdapter = true)
.
Download the latest JAR or depend via Maven:
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi</artifactId>
<version>1.13.0</version>
</dependency>
or Gradle:
implementation("com.squareup.moshi:moshi:1.13.0")
Snapshots of the development version are available in Sonatype's snapshots
repository.
Moshi contains minimally required rules for its own internals to work without need for consumers to embed their own. However if you are using reflective serialization and R8 or ProGuard, you must add keep rules in your proguard configuration file for your reflectively serialized classes.
Annotate enums with @JsonClass(generateAdapter = false)
to prevent them from being removed/obfuscated from your code by R8/ProGuard.
Download Details:
Author: square
Source Code: https://github.com/square/moshi
License: Apache-2.0 license
#java #JSON #kotlin
1654990260
Imagina que eres un desarrollador experto de Android con una idea para una aplicación increíble. Confía en que puede crear fácilmente una aplicación para Android, pero no está tan seguro acerca de iOS.
Si tiene experiencia en el desarrollo de interfaces de usuario móviles para Android, probablemente pueda seguir algunos tutoriales de SwiftUI y salir adelante sin problemas. Pero, ¿qué pasa con el núcleo de la aplicación? Incluso si tiene experiencia con el desarrollo de iOS, volver a escribir el mismo núcleo para iOS puede ser redundante.
Entonces, ¿cómo puede ejecutar su idea con una curva de aprendizaje mínima? Ingrese a Kotlin Multiplatform Mobile o KMM.
Según su sitio web oficial , "Kotlin Multiplatform Mobile (KMM) es un SDK diseñado para simplificar el desarrollo de aplicaciones móviles multiplataforma".
Con KMM, puede escribir el núcleo de su aplicación en Kotlin y usarlo en aplicaciones de Android e iOS. Solo necesita escribir un código específico de la plataforma como UI.
En esta publicación, aprenderemos KMM mediante la creación de una pequeña aplicación para tomar notas con operaciones de bases de datos locales. También veremos cómo se puede reutilizar la lógica comercial común, como las operaciones de base de datos. Pero primero, debemos aclarar algunos requisitos previos.
Primero, necesitamos instalar Android Studio . Si queremos probar el lado de iOS, también necesitaremos instalar Xcode . Podemos compilar y ejecutar la aplicación de Android con código KMM sin Xcode.
A continuación, debemos asegurarnos de tener instalado el último complemento de Kotlin. Vaya a Android Studio Tools y luego coloque el cursor sobre Kotlin . A continuación, haga clic en Configurar actualizaciones del complemento de Kotlin seguido de Verificar de nuevo . Puedes ver una imagen a continuación.
Después de eso, busque el complemento KMM en la sección Complemento del menú Preferencias . Haga clic en Instalar el complemento Kotlin Multiplatform Mobile e instálelo.
No hace falta decir que también necesitamos tener instalado JDK . Tenga cuidado con esto, ya que puede ser un poco complicado. Es posible que debamos configurar la ruta de instalación de JDK en Rutas del sistema para que esto funcione. Afortunadamente, Stack Overflow tiene recursos y respuestas para cualquier problema.
Si tiene que trabajar con SQLDelight , asegúrese de instalar este complemento también para que sea más fácil trabajar con él.
Ahora que el entorno está configurado, creemos un proyecto KMM.
Si ya tiene un proyecto de Android/iOS que desea ampliar con KMM, deberá agregar el módulo KMM a su proyecto de Android, reestructurar el código para mover el código común a módulos compartidos y configurar el proyecto de iOS para que se vincule al Marco KMM. Puede hacer referencia a este proceso aquí , pero ignoremos este caso complicado al comenzar.
Primero, creemos un nuevo proyecto en Android Studio yendo a Archivo -> Nuevo -> Nuevo proyecto . Luego, seleccione Aplicación multiplataforma de Kotlin . Esto aparecerá si finalizamos la configuración del entorno con éxito:
A continuación, complete el nombre del paquete y la ruta del proyecto.
El tercer paso es nuevo y específico de KMM. Aquí, podemos cambiar el nombre de Android, iOS o los nombres de los módulos compartidos. Dado que estamos probando KMM por primera vez, mantengamos estas opciones iguales.
Habilite la casilla de verificación Agregar pruebas de muestra para el módulo compartido si desea probar las ejecuciones de prueba y seleccione Marco regular para la distribución del marco iOS. Debe tener un aspecto como este:
¡Ahora, el proyecto estará listo en poco tiempo! Una vez configurado, podemos cambiar la Vista de archivos de proyecto de Android a Proyecto y ver la estructura completa. Tenga en cuenta que han sucedido muchas cosas detrás de escena, que discutiremos a continuación.
Un proyecto KMM tiene tres módulos: Android, compartido e iOS.
El módulo de Android es nuestro módulo de aplicación normal que se integra en una aplicación de Android. En nuestro caso, lo llamamos androidApp.
Este es un módulo multiplataforma de Kotlin que se compila en una biblioteca de Android y un marco de iOS. El módulo compartido contiene todo el código reutilizable común para la aplicación. Hay tres partes dentro de él: commonMain
, androidMain
y iosMain
.
commonMain
no es específico de la plataforma y contiene todo el código que puede ejecutarse directamente en ambas plataformas. Hay bibliotecas de Kotlin que son compatibles con KMM en ciertos casos de uso. Por ejemplo, usaremos SQLDelight para bases de datos o Ktor para llamadas de red.
En androidMain
, algún código en el módulo común puede necesitar una API o un comportamiento diferente. Este módulo contiene el código específico de Android.
iosMain
es androidMain
la contraparte de . Contiene el código específico de iOS de commonMain
. Tener androidMain
y iosMain
dentro del módulo compartido puede parecer contradictorio, pero quedará claro una vez que hagamos un poco de trabajo práctico.
Este módulo es un proyecto de Xcode que se compila como una aplicación de iOS y consume nuestro módulo KMM compartido como marco. Si recuerda, seleccionamos Regular Framework para iOS en el tercer paso de la sección anterior.
Tenga en cuenta que aunque este módulo se crea dentro de nuestro directorio raíz del proyecto, no está conectado a ninguna otra parte del proyecto (excepto para consumir el módulo compartido).
Como acabamos de ver, el módulo compartido se compila en un marco de iOS y luego lo consume la aplicación de iOS dentro del proyecto raíz. Pero, ¿cómo lo sabe? ¿Necesitamos conectarlos manualmente? ¡No!
Cuando crea un nuevo proyecto KMM en Android Studio, configura automáticamente las rutas de compilación para el marco y otras configuraciones requeridas.
Hay una tarea gradle llamada embedAndSignAppleFrameworkForXcode
que se ejecuta cada vez que se crea la aplicación iOS para generar un nuevo marco.
Para solidificar los conceptos anteriores y ver KMM en acción, construiremos una pequeña aplicación. Específicamente, construiremos una base de datos en el dispositivo y realizaremos algunas operaciones utilizando un código común para Android e iOS.
Específicamente para Android, tendremos una pantalla de inicio con una lista de notas recuperadas de una base de datos en el dispositivo y un CTA para agregar nuevas notas, como se ve a continuación:
Si el usuario hace clic en Agregar , la aplicación lo llevará a una nueva pantalla para agregar una nueva nota y guardarla en la base de datos. La aplicación actualizará la primera pantalla con nuevos datos, como este:
Para iOS, sin embargo, solo tendremos una pantalla en la que borraremos la base de datos en cada lanzamiento, insertaremos nuevas notas automatizadas con marca de tiempo y las buscaremos y mostraremos en una pantalla:
Descargo de responsabilidad: no soy un desarrollador de iOS. Dado que nuestro objetivo aquí es demostrar cómo se puede ejecutar el código de lógica empresarial común en ambas plataformas, realizar las tres operaciones en iOS debería ser suficiente para el propósito de este artículo.
El proyecto se puede clonar desde mi repositorio de GitHub o puede seguirlo y crear un nuevo proyecto. Puede seguir los pasos de la sección "Crear un proyecto KMM" de este artículo y crear una nueva aplicación Android KMM.
No hay muchas dependencias externas para este proyecto, por lo que podemos concentrarnos en cómo se estructuran las dependencias.
Comenzando con el nivel de proyecto build.gradle
, esto necesita las dependencias habituales relacionadas con gradle junto con las dependencias relacionadas con SQLDelight.
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.squareup.sqldelight:gradle-plugin:$sqlDelightVersion")
El nivel de la aplicación build.gradle
también tiene todos los elementos estándar, pero hay un requisito adicional. Tenemos que agregar una dependencia en el módulo compartido en el dependencies
bloque:
implementation(project(":shared"))
El módulo compartido build.gradle
tiene todos los cambios principales:
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.squareup.sqldelight")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
val coroutinesVersion = "1.6.1"
val ktorVersion = "1.6.1"
val sqlDelightVersion: String by project
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
}
}
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
}
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
compileSdk = 31
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 21
targetSdk = 31
}
}
sqldelight {
database("KmmDemoDB") {
packageName = "com.outliers.kmmdemo.shared.cache"
}
}
Con la excepción de la declaración de dependencias externas, todo lo demás se agrega automáticamente al crear un nuevo proyecto KMM. Sin embargo, necesitamos comprender varios bloques para saber dónde agregar dependencias.
Comenzando con el plugins
bloque, observe que hemos aplicado el library
complemento junto con el multiplatform
complemento agregado automáticamente. El sqldelight
complemento es para nuestro SDK de base de datos y es posible que no sea necesario para otros proyectos y, por lo tanto, debe agregarse manualmente.
SQLDelight también necesita el último bloque para definir el nombre de la base de datos y el paquete/ruta donde se definen las consultas. Las dependencias necesarias para el código escrito en el androidMain
módulo compartido se declaran en el androidMain
bloque interior sourceSets
. Lo mismo se aplica para iosMain
y commonMain
.
Ahora veamos el código fuente. El quid aquí es el módulo compartido. Configuraremos la base de datos SDK, SQLDelight.
Primero, cree una ruta de directorio sqldelight/your-package-name/shared/cache
dentro de la commonMain
carpeta del módulo compartido, donde your-package-name
está el nombre del paquete que definió al crear el proyecto (en este caso, la ruta es sqldelight/com/outliers/kmmdemo/shared/cache
).
Aquí, necesitamos crear un archivo <your database name>.sq
que contenga todas nuestras consultas SQL, como insert
o select all
. Para esta demostración, nombré el archivo KmmDemoDB.sq
.
El nombre de la base de datos y el nombre del paquete deben coincidir con lo que hemos definido en el sqldelight
bloque en shared/build.gradle
. SQLDelight usa este archivo para generar código correspondiente a las consultas proporcionadas en ~/shared/build/generated/sqldelight
.
Si encuentra esto confuso, tómese un tiempo para navegar por el proyecto e identificar los archivos que estamos discutiendo. Puede estar tranquilo con el hecho de que esto solo es específico de la biblioteca SQLDelight y, por lo tanto, puede aprenderse a fondo de su documentación.
Para ejecutar realmente las consultas SQL en el código común de Kotlin, necesitamos un objeto llamado SqlDriver
. Pero esto SqlDriver
se crea utilizando diferentes API para plataformas Android e iOS.
Aquí viene el papel de androidMain
y iosMain
. Primero definiremos una clase llamada DatabaseDriverFactory
, declarándola como expect
en conjunto fuente común ( commonMain
):
expect class DatabaseDriverFactory {
fun createDriver(): SqlDriver
}
La expect
palabra clave le dice al compilador que busque una implementación real de esta clase en el conjunto de fuentes específico de la plataforma ( androidMain
y iosMain
) en el mismo paquete ( commonMain/com/outliers/kmmdemo/shared/cache
) en todos los conjuntos de fuentes.
Esto permite el uso de API específicas de la plataforma para crear SqlDriver
, después de lo cual todo es igual. Así es como se ven las implementaciones reales de androidMain
y iosMain
:
actual class DatabaseDriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(KmmDemoDB.Schema, context, "notes.db")
}
}
actual class DatabaseDriverFactory {
actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(KmmDemoDB.Schema, "notes.db")
}
}
Tenga en cuenta que usamos diferentes API para crear y devolver una instancia de SqlDriver
, a saber, AndroidSqliteDriver
y NativeSqliteDriver
. Estas API están disponibles para androidMain
y a iOSMain
través de las diferentes dependencias declaradas para cada fuente en el build.gradle
archivo del módulo compartido.
Ahora, crearemos una clase contenedora llamada Database
en el mismo paquete que crea internamente un SqlDriver
objeto usando DatabaseDriverFactory
y expone funciones para realizar operaciones de base de datos.
class Database(databaseDriverFactory: DatabaseDriverFactory) {
private val database = KmmDemoDB(databaseDriverFactory.createDriver())
private val dbQuery = database.kmmDemoDBQueries
internal fun getAllNotes(): List<Note> {
return dbQuery.selectAllNotes().executeAsList()
}
internal fun getLastNote(): Note {
return dbQuery.selectLastNote().executeAsOne()
}
internal fun insertNote(title: String, body: String?) {
return dbQuery.insertNote(title, body)
}
internal fun deleteAll() {
return dbQuery.deleteAll()
}
}
A continuación, creemos otra clase raíz de SDK. Esto crea el Database
objeto y expone todas las operaciones. Este se convertirá en nuestro punto de entrada a la biblioteca compartida para aplicaciones de Android/iOS.
class KmmSDK(dbDriverFactory: DatabaseDriverFactory) {
private val database: Database = Database(dbDriverFactory)
fun getAllNotes(): List<Note> {
return database.getAllNotes()
}
fun getLastNote(): Note {
return database.getLastNote()
}
fun insertNote(title: String, body: String?) {
database.insertNote(title, body)
}
fun deleteAll() {
database.deleteAll()
}
}
Esta es la clase que las aplicaciones de Android e iOS instanciarán y realizarán el trabajo principal. Esta clase toma una instancia de DatabseDriverFactory
. Por lo tanto, cuando iOS instancia esta clase, el compilador seleccionará la implementación de iosMain
.
Ahora, todo lo que tiene que hacer es importar KmmSDK
en Android o shared
en iOS y llamar a sus métodos, como se muestra arriba. El resto es todo el desarrollo de la interfaz de usuario, con el que estamos demasiado familiarizados. Pase la lista de notas a un RecyclerViewAdapter
y muéstrela en la pantalla. También se implementa una lógica similar para iOS.
¡Uf! Hemos cubierto mucho terreno aquí. Pero, todavía hay una cosa para reflexionar: si hemos escrito todo nuestro código en Kotlin, ¿cómo lo entiende iOS?
Es por el compilador de Kotlin. El compilador de Kotlin primero convierte el código de Kotlin en una representación intermedia (código de bytes para JVM de Android), que, a su vez, se convierte en el código nativo de la plataforma.
¡Y eso es! Hemos cubierto la mayoría de las cosas que uno necesita para comenzar a trabajar con Kotlin Multiplatform.
Esta historia se publicó originalmente en https://blog.logrocket.com/building-cross-platform-mobile-apps-kotlin-multiplatform/
1654990140
あなたがキラーアプリのアイデアを持ったAndroidのエキスパートであると想像してみてください。Android用のアプリを簡単に作成できると確信していますが、iOSについてはあまり確信がありません。
Android用のモバイルUIの開発経験がある場合は、おそらくいくつかのSwiftUIチュートリアルに従って、問題なく実行できます。しかし、アプリのコアはどうですか?iOS開発の経験がある場合でも、iOS用に同じコアを書き直すことは冗長になる可能性があります。
では、最小限の学習曲線でアイデアを実行するにはどうすればよいでしょうか。Kotlin Multiplatform Mobile、またはKMMを入力します。
彼らの公式ウェブサイトによると、「Kotlin Multiplatform Mobile(KMM)は、クロスプラットフォームモバイルアプリケーションの開発を簡素化するために設計されたSDKです。」
KMMを使用すると、アプリのコアをKotlinで記述し、AndroidアプリケーションとiOSアプリケーションの両方で使用できます。UIのようなプラットフォーム固有のコードを記述するだけで済みます。
この投稿では、ローカルデータベース操作を使用して小さなメモを取るアプリケーションを構築することにより、KMMについて学習します。また、データベース操作などの一般的なビジネスロジックを再利用する方法についても説明します。ただし、最初に、いくつかの前提条件をクリアする必要があります。
まず、AndroidStudioをインストールする必要があります。iOS側を試してみたい場合は、Xcodeもインストールする必要があります。Xcodeを使用せずにKMMコードを使用してAndroidアプリをビルドして実行できます。
次に、最新のKotlinプラグインがインストールされていることを確認する必要があります。Android Studio Toolsに移動し、Kotlinにカーソルを合わせます。次に、[Kotlinプラグインの更新の構成]をクリックしてから、[もう一度確認]をクリックします。あなたは下のビジュアルを見ることができます。
その後、 [設定]メニューの[プラグイン]セクションでKMMプラグインを検索します。[ Kotlinマルチプラットフォームモバイルプラグインのインストール]をクリックしてインストールします。
言うまでもなく、JDKもインストールする必要があります。少し注意が必要ですので、注意してください。これを機能させるには、システムパスでJDKインストールパスを設定する必要がある場合があります。幸いなことに、StackOverflowにはあらゆる問題に対するリソースと回答があります。
SQLDelightを使用する必要がある場合は、このプラグインもインストールして、操作しやすくしてください。
環境が設定されたので、KMMプロジェクトを作成しましょう。
KMMで拡張するAndroid/iOSプロジェクトがすでにある場合は、KMMモジュールをAndroidプロジェクトに追加し、コードを再構築して共通コードを共有モジュールに移動し、iOSプロジェクトを構成してKMMフレームワーク。ここでこのプロセスを参照できますが、最初はこの複雑なケースを無視しましょう。
まず、 [ファイル] ->[新規] ->[新しいプロジェクト]に移動して、AndroidStudioで新しいプロジェクトを作成しましょう。次に、KotlinMultiplatformAppを選択します。これは、環境のセットアップが正常に完了すると表示されます。
次に、パッケージ名とプロジェクトパスを入力します。
3番目のステップは新しく、KMMに固有です。ここで、Android、iOS、または共有モジュール名の名前を変更できます。KMMを初めて試すので、これらのオプションを同じに保ちましょう。
テストの実行を試して、iOSフレームワーク配布用の通常のフレームワークを選択する場合は、[共有モジュールのサンプルテストを追加する]チェックボックスを有効にします。次のようになります。
これで、プロジェクトはすぐに準備が整います!設定が完了したら、プロジェクトファイルビューをAndroidからProjectに変更して、完全な構造を表示できます。舞台裏では多くのことが起こっていることを覚えておいてください。これについては次に説明します。
KMMプロジェクトには、Android、共有、iOSの3つのモジュールがあります。
Androidモジュールは、Androidアプリケーションに組み込まれる通常のアプリモジュールです。この例では、androidAppという名前を付けました。
これは、AndroidライブラリとiOSフレームワークにコンパイルされるKotlinマルチプラットフォームモジュールです。共有モジュールは、アプリのすべての一般的な再利用可能なコードを保持します。commonMain
その中には、、、androidMain
およびの3つの部分がありますiosMain
。
commonMain
プラットフォーム固有ではなく、両方のプラットフォームで直接実行できるすべてのコードを保持します。特定のユースケースでKMMと互換性のあるKotlinライブラリがあります。たとえば、データベースにはSQLDelightを使用し、ネットワーク呼び出しにはKtorを使用します。
ではandroidMain
、共通モジュールの一部のコードで異なるAPIまたは動作が必要になる場合があります。このモジュールは、Android固有のコードを保持します。
iosMain
のandroidMain
対応物です。iOS固有のコードを保持しますcommonMain
。共有モジュールの内部にあることは直感に反するように思えるかもしれませんが、実際に作業を行うandroidMain
とiosMain
明らかになります。
このモジュールは、iOSアプリケーションとしてビルドされ、共有KMMモジュールをフレームワークとして使用するXcodeプロジェクトです。思い出してください。前のセクションの3番目のステップで、iOS用の通常のフレームワークを選択しました。
このモジュールはルートプロジェクトディレクトリ内に作成されますが、プロジェクトの他の部分には接続されていないことに注意してください(共有モジュールを使用する場合を除く)。
先ほど説明したように、共有モジュールはiOSフレームワークにコンパイルされ、ルートプロジェクト内のiOSアプリによって使用されます。しかし、それはどのようにしてそれを知るのでしょうか?それらを手動で接続する必要がありますか?いいえ!
Android Studioで新しいKMMプロジェクトを作成すると、フレームワークのビルドパスとその他の必要な設定が自動的に構成されます。
embedAndSignAppleFrameworkForXcode
新しいフレームワークを生成するためにiOSアプリがビルドされるたびに実行されるというgradleタスクがあります。
上記の概念を固め、KMMの動作を確認するために、小さなアプリケーションを作成します。具体的には、デバイス上のデータベースを構築し、AndroidとiOSの両方に共通のコードを使用していくつかの操作を実行します。
特にAndroidの場合、デバイス上のデータベースから取得したメモのリストと、新しいメモを追加するためのCTAを含む起動画面が表示されます(以下を参照)。
ユーザーが[追加]をクリックすると、アプリはユーザーを新しい画面に移動して新しいメモを追加し、データベースに保存します。アプリは、次のように最初の画面を新しいデータで更新します。
ただし、iOSの場合、起動ごとにデータベースをクリアし、タイムスタンプ付きの新しい自動メモを挿入し、それらをフェッチして画面に表示する画面は1つだけです。
免責事項–私はiOS開発者ではありません。ここでの目標は、両方のプラットフォームで共通のビジネスロジックコードを実行する方法を示すことであるため、この記事の目的には、iOSで3つの操作すべてを実行するだけで十分です。
プロジェクトは私のGitHubリポジトリから複製することも、フォローして新しいプロジェクトを構築することもできます。この記事の「KMMプロジェクトの作成」セクションの手順に従って、新しいAndroidKMMアプリケーションを作成できます。
このプロジェクトには多くの外部依存関係がないため、依存関係の構造に集中できます。
プロジェクトレベルから始めてbuild.gradle
、これにはSQLDelight関連の依存関係に加えて通常のgradle関連の依存関係が必要です。
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.squareup.sqldelight:gradle-plugin:$sqlDelightVersion")
アプリレベルbuild.gradle
にもすべての標準アイテムがありますが、追加の要件が1つあります。dependencies
ブロック内の共有モジュールへの依存関係を追加する必要があります。
implementation(project(":shared"))
共有モジュールbuild.gradle
には、すべての主要な変更があります。
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.squareup.sqldelight")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
val coroutinesVersion = "1.6.1"
val ktorVersion = "1.6.1"
val sqlDelightVersion: String by project
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
}
}
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
}
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
compileSdk = 31
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 21
targetSdk = 31
}
}
sqldelight {
database("KmmDemoDB") {
packageName = "com.outliers.kmmdemo.shared.cache"
}
}
外部依存関係の宣言を除いて、他のすべては、新しいKMMプロジェクトの作成中に自動的に追加されます。ただし、依存関係を追加する場所を知るには、さまざまなブロックを理解する必要があります。
ブロックから始めて、自動的に追加されたプラグインと一緒にプラグインplugins
を適用したことに注意してください。プラグインはデータベースSDK用であり、他のプロジェクトでは必要ない場合があるため、手動で追加する必要があります。librarymultiplatformsqldelight
SQLDelightには、データベース名とクエリが定義されているパッケージ/パスを定義するための最後のブロックも必要です。androidMain
共有モジュールので記述されたコードに必要な依存関係は、androidMain
内のブロックで宣言されsourceSets
ます。iosMain
同じことがとにも当てはまりますcommonMain
。
それでは、ソースコードを見てみましょう。ここで重要なのは共有モジュールです。データベースSDK、SQLDelightをセットアップします。
まず、共有モジュールsqldelight/your-package-name/shared/cache
のフォルダー内にディレクトリパスを作成します。ここで、はプロジェクトの作成時に定義したパッケージ名です(この場合、パスはです)。commonMainyour-package-namesqldelight/com/outliers/kmmdemo/shared/cache
ここでは、または<your database name>.sq
などのすべてのSQLクエリを保持するファイルを作成する必要があります。このデモでは、ファイルに。という名前を付けました。insertselect allKmmDemoDB.sq
sqldelight
データベース名とパッケージ名は、のブロックで定義したものと一致する必要がありますshared/build.gradle
。SQLDelightはこのファイルを使用して、で提供されたクエリに対応するコードを生成します~/shared/build/generated/sqldelight
。
これがわかりにくい場合は、時間をかけてプロジェクト内を移動し、話し合っているファイルを特定してください。これはSQLDelightライブラリにのみ固有であり、ドキュメントから完全に学ぶことができるという事実に安心できます。
Kotlin共通コードでSQLクエリを実際に実行するには、という名前のオブジェクトが必要SqlDriver
です。ただし、これSqlDriver
はAndroidプラットフォームとiOSプラットフォームで異なるAPIを使用して作成されています。
ここにとの役割がandroidMain
ありiosMain
ます。まず、と呼ばれるクラスを定義し、それを共通のソースセット()のDatabaseDriverFactory
ように宣言します。expectcommonMain
expect class DatabaseDriverFactory {
fun createDriver(): SqlDriver
}
このキーワードは、すべてのソースセットの同じパッケージ( )にあるプラットフォーム固有のソースセット(および)expect
でこのクラスの実際の実装を探すようにコンパイラーに指示します。androidMainiosMaincommonMain/com/outliers/kmmdemo/shared/cache
これにより、プラットフォーム固有のAPIを使用して作成できSqlDriver
、その後はすべて同じになります。の実際の実装は次のようにandroidMain
なりiosMain
ます。
actual class DatabaseDriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(KmmDemoDB.Schema, context, "notes.db")
}
}
actual class DatabaseDriverFactory {
actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(KmmDemoDB.Schema, "notes.db")
}
}
のインスタンスを作成して返すためにさまざまなAPIを使用していることに注意してください。SqlDriver
つまり、AndroidSqliteDriver
とNativeSqliteDriver
。これらのAPIは、共有モジュールのファイル内のソースセットごとに宣言されたさまざまな依存関係androidMain
で利用できます。iOSMainbuild.gradle
Database
次に、同じパッケージで呼び出されるラッパークラスを作成します。このクラスは、データベース操作を実行する関数をSqlDriver
使用してオブジェクトを内部的に作成しDatabaseDriverFactory
、公開します。
class Database(databaseDriverFactory: DatabaseDriverFactory) {
private val database = KmmDemoDB(databaseDriverFactory.createDriver())
private val dbQuery = database.kmmDemoDBQueries
internal fun getAllNotes(): List<Note> {
return dbQuery.selectAllNotes().executeAsList()
}
internal fun getLastNote(): Note {
return dbQuery.selectLastNote().executeAsOne()
}
internal fun insertNote(title: String, body: String?) {
return dbQuery.insertNote(title, body)
}
internal fun deleteAll() {
return dbQuery.deleteAll()
}
}
次に、別のSDKルートクラスを作成しましょう。これにより、Database
オブジェクトが作成され、すべての操作が公開されます。これが、Android/iOSアプリの共有ライブラリへのエントリポイントになります。
class KmmSDK(dbDriverFactory: DatabaseDriverFactory) {
private val database: Database = Database(dbDriverFactory)
fun getAllNotes(): List<Note> {
return database.getAllNotes()
}
fun getLastNote(): Note {
return database.getLastNote()
}
fun insertNote(title: String, body: String?) {
database.insertNote(title, body)
}
fun deleteAll() {
database.deleteAll()
}
}
これは、AndroidおよびiOSアプリがインスタンス化してコア作業を実行するクラスです。このクラスはのインスタンスを取りますDatabseDriverFactory
。したがって、iOSがこのクラスをインスタンス化すると、コンパイラはから実装を選択しますiosMain
。
KmmSDK
これで、上記のように、Androidまたはshared
iOSにインポートして、それらのメソッドを呼び出すだけです。残りはすべてUI開発であり、私たちがよく知っているものです。ノートリストをに渡し、RecyclerViewAdapter
画面に表示します。同様のロジックがiOSにも実装されています。
ふぅ!ここでは多くのことをカバーしました。しかし、まだ検討すべきことが1つあります。それは、すべてのコードをKotlinで記述した場合、iOSはそれをどのように理解するのでしょうか。
これは、Kotlinコンパイラが原因です。Kotlinコンパイラは、最初にKotlinコードを中間表現(Android JVMのバイトコード)に変換します。中間表現は、プラットフォームのネイティブコードに変換されます。
以上です!KotlinMultiplatformでの作業を開始するために必要なほとんどのことをカバーしました。
このストーリーは、もともとhttps://blog.logrocket.com/building-cross-platform-mobile-apps-kotlin-multiplatform/で公開されました
1654946460
En esta guía, hablaremos sobre RegEx y cómo se usa en Kotlin. RegEx significa "expresión regular". En términos sencillos, comprueba si una cadena coincide con un patrón. Las expresiones regulares son útiles en casos de validación de entrada.
¿Por qué es esto un gran problema? En lugar de tener numerosos operadores IF y ELSE, RegEx maneja todas estas validaciones a la vez.
Eso nos lleva al objetivo de esta guía. En este artículo, lo guiaremos sobre cómo usar la expresión regular como medio de validación en Kotlin mediante la creación de una aplicación simple para validar un número de teléfono móvil.
Recomiendo a los lectores de esta guía tener lo siguiente:
(Nota: también deberá tener su estudio de Android en funcionamiento en su sistema operativo)
Trabajar con patrones de expresiones regulares a menudo puede ser confuso, por lo que aprenderemos a buscar patrones en una cadena usando RegEx. A continuación se muestran algunos de los patrones RegEx en Kotlin:
Para verificar un patrón de entrada con RegEx, cree una clase RegEx con el nombre de variable "patrón". Esta variable contendrá el patrón que queremos hacer coincidir.
De manera similar, crearemos otra variable con el nombre "resultado" para especificar la cadena RegEx. A continuación se muestra una implementación de esta explicación:
fun main ()
{
var pattern = Regex("wa");
var result = pattern.containsMatchIn("Muyiwa");
println(result);
}
La salida es verdadera ya que "wa" está dentro de la cadena de expresión regular "Muyiwa".
(Nota: esto distingue entre mayúsculas y minúsculas, por lo que "WA" es diferente de "wa", pero podemos pasar por alto esta situación usando IGNORE_CASE
)
Esta sección le mostrará cómo comprobar patrones en una cadena independientemente de la distinción entre mayúsculas y minúsculas. Para ello, pasaremos RegexOption.IGNORE_CASE
como parámetro al constructor RegEx. Por lo tanto, el código será el siguiente:
fun main ()
{
var pattern = "WA".toRegex(RegexOption.IGNORE_CASE)
var result = pattern.containsMatchIn("Muyiwa");
println(result);
}
Hay procesos similares entre los dos (patrón anterior), con la única diferencia de que si hay un patrón, el código anterior lo reconocerá independientemente de su caso.
Este proyecto le mostrará cómo validar un número de teléfono móvil usando RegEx. Para facilitar el flujo, pondremos los procesos paso a paso. Por lo tanto, para comenzar, siga los pasos a continuación en consecuencia:
Primero, inicie su estudio de Android y seleccione "Nuevo proyecto".
Después de esto, seleccione "Actividad vacía" como plantilla de su proyecto y haga clic en "Siguiente".
En esta página, use cualquier nombre de su elección; para este proyecto, lo he llamado "PhoneNumberValidation". Seleccione el idioma de su elección (Kotlin) y haga clic en "Finalizar".
Hagamos un resumen rápido de lo que queremos hacer: queremos crear un botón simple y un área de "Editar texto". En este proceso, queremos que el botón esté habilitado una vez que el usuario ingresa un número de teléfono válido, y si es un número de teléfono no válido, el mensaje que se muestra debe ser "Número de teléfono no válido".
Ahora, podemos comenzar con el diseño.
Primero, en su nuevo proyecto, haga clic en activity_main.xml y seleccione "Mostrar interfaz de usuario del sistema" en la sección de diseño. Haga clic en el icono del ojo para encontrar esto.
A continuación, haga clic en "Dividir" y elimine la vista de texto predeterminada, ya que no la queremos en esa posición. Haga clic en la paleta, luego seleccione "Texto" y arrastre y suelte el texto del teléfono en la interfaz de usuario del sistema (haga clic en el icono de varita mágica para agregar la restricción a EditText).
Agregue una sugerencia a EditText con el texto "Ingresar número de teléfono". Por razones más sencillas, cambiaremos la ID para editar. Por lo tanto, nuestro oyente EditText se verá así:
(Nota: Depende de usted si desea cambiar la ID predeterminada en EditText)
A continuación, necesitaremos un botón. Para hacer esto, seguiremos un proceso similar a cómo agregamos el área de texto, con la diferencia de que estamos agregando un botón en su lugar. En este caso, necesitamos que el botón esté deshabilitado; por lo tanto, establecemos la propiedad habilitada como "falsa".
android:enabled="false"
Entonces, el código para el botón será el siguiente:
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Button"
android:enabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit" />
Antes de continuar, necesitaremos vincular nuestra vista de Android con Kotlin. En el pasado, realizar esta operación era bastante fácil usando los sintéticos de Kotlin.
Todo lo que tiene que hacer es agregar el siguiente complemento a build.gradle(:app) y llamar a la vista por su ID en el archivo Kotlin:
id 'kotlin-android'
id 'kotlin-android-extensions'
Sin embargo, esto ha quedado obsoleto y no está incluido en los nuevos proyectos de Android Studio de forma predeterminada.
El problema con los sintéticos de Kotlin era que, aunque podíamos llamar a estas vistas por su ID, todo esto podía ser potencialmente nulo; lo que significa que podríamos acceder a vistas que no pertenecen al diseño.
Por otro lado, el enlace de vista rompe esa brecha porque garantiza que las vistas a las que se accede en una actividad o fragmento no sean nulas. Por lo tanto, no hay forma de que ocurra una excepción de puntero nulo.
(Nota: el enlace de datos es una alternativa al enlace de vista, pero el enlace de vista es más simple y el enlace de vista solo vinculará sus vistas)
Aquí hay un método paso a paso sobre cómo usar el enlace de vista en sus vistas:
Primero, dirígete a tu build.gradle(:app), porque tienes que habilitar explícitamente que deseas usar el enlace de vista en tu proyecto.
En el bloque de Android, agregue el nuevo bloque a continuación y sincronice su archivo Gradle:
buildFeatures {
viewBinding true
}
Dirígete al archivo MainActivity.kt. Allí, queremos hacer referencia al EditText.
El enlace de vista genera una clase de enlace utilizada para acceder a nuestras vistas. Entonces, crearemos una private lateinit var
llamada binding
con el tipo ActivityMainBinding (este es el enlace de vista de clase generado para nosotros).
Básicamente, para cada archivo XML en nuestro proyecto, el enlace de vista hará eso, y no necesitamos reconstruir nuestro proyecto ya que no se requiere el procesamiento de anotaciones.
Ahora que tenemos el enlace, para inicializarlo, usaremos el siguiente código:
binding = ActivityMainBinding.inflate(layoutInflater)
layoutInflater
básicamente agrega vistas de un grupo a otro. Finalmente, estableceremos la vista de contenido en binding.root usando el siguiente código:
setContentView(binding.root)
Ahora para tener acceso a la vista, use el código binding.id
(ID de las vistas). Ahora estamos listos para irnos.
Queremos que el botón se habilite una vez que el usuario ingrese un número de móvil válido. Para hacer esto, dirígete al archivo MainActivity.kt. Tendremos que escribir el código de la tecla pulsada. A continuación se muestra el código Kotlin requerido para todo el proceso:
package com.example.phonenumbervalidation
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import com.example.phonenumbervalidation.databinding.ActivityMainBinding
import java.util.regex.Pattern
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// beginning
binding.edit.addTextChangedListener(object:TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (mobileValidate(binding.edit.text.toString()))
binding.button2.isEnabled=true
else{
binding.button2.isEnabled=false
binding.edit.setError("Invalid Phone number")
}
}
override fun afterTextChanged(p0: Editable?) {
}
})
}
private fun mobileValidate(text: String?): Boolean {
val p = Pattern.compile("\[0\][7-9]\[0-1\][0-9]{8}")
val m = p.matcher(text)
return m.matches()
}
}
TextWatcher
supervisa los cambios realizados en EditText utilizando cualquiera de sus tres métodos de devolución de llamada:
beforeTextChanged
onTextChanged
afterTextChanged
Aquí hay un enlace a un recurso si desea obtener información sobre estos métodos. En onTextChanged
, creamos una instrucción if para verificar la función mobileValidate con el argumento binding.edit. texto .toString().
Inicialmente, hacer esto generará un error ya que no tenemos una función llamada mobileValidate, así que presione Alt+Enter para crear automáticamente una función en MainActivity. Cambie el argumento de Editable
a String
ya que desea que la entrada sea una cadena.
En la función mobileValidate, queremos que nuestro texto coincida con un cierto patrón: un número de teléfono estándar de Nigeria (generalmente 070, 080, 081, 090, 091 y un total de 11 dígitos).
Por lo tanto, compilamos un patrón para cuatro intervalos diferentes, con los tres primeros teniendo solo una combinación, y dividimos el último intervalo en ocho combinaciones de intervalos 0-9. Entonces, básicamente, cualquier dígito fuera de cada intervalo marcará el número como no válido y el botón permanecerá deshabilitado.
A continuación, declararemos nuestra instancia de comparación usando la función p.matcher(). Luego, analizaremos la entrada en nuestra función de comparación y devolveremos m.matches(). El resultado devuelve verdadero si nuestra entrada coincide con el patrón y el botón está habilitado, y falso si no hay ninguna coincidencia.
Volviendo a la declaración if, el botón debe estar habilitado si la condición del botón sigue siendo verdadera. También tenemos una sentencia else en caso de que la condición cambie a falsa. Configuramos un mensaje de error para lanzar una notificación de que "El número no es válido".
Después de esto, puede ejecutar la aplicación en su emulador deseado para probarla.
La versatilidad de RegEx lo convierte en una herramienta esencial en numerosos lenguajes de programación, y su capacidad para verificar patrones lo ha convertido en una herramienta eficiente para la validación.
Sus ventajas en términos de versatilidad también vienen con múltiples desventajas: su sintaxis compleja, por un lado, que hace que RegEx sea algo difícil incluso para los programadores veteranos.
No obstante, es absolutamente ventajoso tener el conocimiento de ReGex en su repertorio, porque un día, ¡será útil mientras esté en el espacio de desarrollo!
Esta historia se publicó originalmente en https://blog.logrocket.com/guide-regular-expression-kotlin/
1654946340
このガイドでは、RegExとKotlinでの使用方法について説明します。RegExは「正規表現」の略です。素人の言葉で言えば、文字列がパターンと一致するかどうかをチェックします。正規表現は、入力の検証の場合に役立ちます。
なぜこれが大したことなのですか?多数のIFおよびELSE演算子を使用する代わりに、RegExはこれらの検証を一度に処理します。
それがこのガイドの目的につながります。この記事では、携帯電話番号を検証するための簡単なアプリを作成することにより、Kotlinでの検証の手段として正規表現を使用する方法について説明します。
このガイドの読者には、次のものをお勧めします。
(注:Android Studioをオペレーティングシステムで稼働させる必要もあります)
正規表現のパターンは扱いにくいことが多いため、RegExを使用して文字列内のパターンをチェックする方法を学習します。以下は、Kotlinの正規表現パターンの一部です。
RegExで入力パターンを確認するには、変数名「pattern」でRegExクラスを作成します。この変数には、一致させたいパターンが含まれます。
同様に、「result」という名前の別の変数を作成して、RegEx文字列を指定します。以下は、この説明の実装です。
fun main ()
{
var pattern = Regex("wa");
var result = pattern.containsMatchIn("Muyiwa");
println(result);
}
「wa」は正規表現文字列「Muyiwa」内にあるため、出力はtrueです。
(注:これは大文字と小文字が区別されるため、「WA」は「wa」とは異なりますが、を使用してこの状況を見逃すことができますIGNORE_CASE
)
このセクションでは、大文字と小文字の区別に関係なく、文字列のパターンをチェックする方法を示します。これを行うにはRegexOption.IGNORE_CASE
、パラメーターとしてRegExコンストラクターに渡します。したがって、コードは次のようになります。
fun main ()
{
var pattern = "WA".toRegex(RegexOption.IGNORE_CASE)
var result = pattern.containsMatchIn("Muyiwa");
println(result);
}
2つの間に同様のプロセスがあります(上記のパターン)。唯一の違いは、パターンがある場合、上記のコードはケースに関係なくそれを認識します。
このプロジェクトでは、RegExを使用して携帯電話番号を検証する方法を紹介します。フローを簡単にするために、プロセスを段階的に説明します。したがって、開始するには、以下の手順に従ってください。
まず、Android Studioを起動し、「新しいプロジェクト」を選択します。
この後、プロジェクトテンプレートとして「空のアクティビティ」を選択し、「次へ」をクリックします。
このページでは、任意の名前を使用します—このプロジェクトでは、「PhoneNumberValidation」と呼んでいます。選択した言語(Kotlin)を選択し、[完了]をクリックします。
やりたいことを簡単に説明しましょう。簡単なボタンと「テキストの編集」領域を作成します。このプロセスでは、ユーザーが有効な電話番号を入力したらボタンを有効にします。無効な電話番号の場合、表示されるメッセージは「無効な電話番号」です。
これで、設計から始めることができます。
まず、新しいプロジェクトで、activity_main.xmlをクリックし、デザインセクションで[システムUIの表示]を選択します。これを見つけるには、目のアイコンをクリックします。
次に、「分割」をクリックし、デフォルトのテキストビューを削除します。その位置には配置したくないためです。パレットをクリックし、[テキスト]を選択して、電話のテキストをシステムUIにドラッグアンドドロップします(ワンドアイコンをクリックして、編集テキストに制約を追加します)。
「電話番号を入力してください」というテキストを使用して、EditTextにヒントを追加します。より簡単な理由で、編集するIDを変更します。したがって、EditTextリスナーは次のようになります。
(注:EditTextのデフォルトIDを変更するかどうかは、ユーザー次第です)
次に、ボタンが必要になります。これを行うには、テキスト領域を追加したのと同様のプロセスに従いますが、代わりにボタンを追加する点が異なります。この場合、ボタンを無効にする必要があります。したがって、enabledプロパティを「false」に設定します。
android:enabled="false"
したがって、ボタンのコードは次のようになります。
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Button"
android:enabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit" />
先に進む前に、AndroidビューをKotlinにバインドする必要があります。以前は、Kotlin合成を使用すると、この操作を実行するのはかなり簡単でした。
次のプラグインをbuild.gradle(:app)に追加し、KotlinファイルのIDでビューを呼び出すだけです。
id 'kotlin-android'
id 'kotlin-android-extensions'
ただし、これは非推奨になり、デフォルトでは新しいAndroidStudioプロジェクトに含まれていません。
Kotlin合成の問題は、これらのビューをIDで呼び出すことができたとしても、これらすべてがnullになる可能性があることでした。つまり、レイアウトに属していないビューにアクセスできます。
一方、ビューバインディングは、アクティビティまたはフラグメントでアクセスされるビューがnullにならないようにするため、そのギャップを解消します。したがって、nullポインタ例外が発生する可能性はありません。
(注:データバインディングはビューバインディングの代替手段ですが、ビューバインディングはたまたま単純であり、ビューバインディングはビューのみをバインドします)
ビューでビューバインディングを使用する方法のステップバイステップの方法は次のとおりです。
まず、build.gradle(:app)に進みます。これは、プロジェクトでビューバインディングを使用することを明示的に有効にする必要があるためです。
Androidブロックで、以下の新しいブロックを追加して、Gradleファイルを同期します。
buildFeatures {
viewBinding true
}
MainActivity.ktファイルに移動します。そこでは、EditTextを参照します。
ビューバインディングは、ビューへのアクセスに使用されるバインディングクラスを生成します。したがって、ActivityMainBindingタイプのprivate lateinit var
呼び出しを作成します(これは、生成されたクラスビューバインディングです)。binding
基本的に、プロジェクト内のすべてのXMLファイルに対して、ビューバインディングがそれを行います。注釈処理が必要ないため、プロジェクトを再構築する必要はありません。
バインディングができたので、それを初期化するために、以下のコードを使用します。
binding = ActivityMainBinding.inflate(layoutInflater)
layoutInflater
基本的に、あるグループから別のグループにビューを追加します。最後に、以下のコードを使用して、コンテンツビューをbinding.rootに設定します。
setContentView(binding.root)
ビューにアクセスするには、コードbinding.id
(ビューのID)を使用します。今、私たちは行ってもいいです。
ユーザーが有効な携帯電話番号を入力したら、ボタンを有効にします。これを行うには、MainActivity.ktファイルに移動します。押されたキーのコードを書く必要があります。以下は、プロセス全体に必要なKotlinコードです。
package com.example.phonenumbervalidation
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import com.example.phonenumbervalidation.databinding.ActivityMainBinding
import java.util.regex.Pattern
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// beginning
binding.edit.addTextChangedListener(object:TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (mobileValidate(binding.edit.text.toString()))
binding.button2.isEnabled=true
else{
binding.button2.isEnabled=false
binding.edit.setError("Invalid Phone number")
}
}
override fun afterTextChanged(p0: Editable?) {
}
})
}
private fun mobileValidate(text: String?): Boolean {
val p = Pattern.compile("\[0\][7-9]\[0-1\][0-9]{8}")
val m = p.matcher(text)
return m.matches()
}
}
TextWatcher
次の3つのコールバックメソッドのいずれかを使用して、EditTextに加えられた変更を監視します。
beforeTextChanged
onTextChanged
afterTextChanged
これらの方法について知りたい場合は、リソースへのリンクを次に示します。ではonTextChanged
、引数binding.editを使用してmobileValidate関数をチェックするifステートメントを作成します。text .toString()。
mobileValidateという関数がないため、最初はこれを行うとエラーがスローされます。そのため、Alt + Enterキーを押して、MainActivityに関数を自動的に作成します。 入力を文字列にする必要があるため、引数をからEditable
に変更します。String
mobileValidate関数では、テキストを特定のパターン(標準のナイジェリアの電話番号(通常は070、080、081、090、091、合計11桁))に一致させる必要があります。
したがって、最初の3つは1つの組み合わせのみで、最後の間隔を0〜9の間隔の8つの組み合わせに分割して、4つの異なる間隔のパターンをコンパイルします。したがって、基本的に、各間隔の外側の数字は番号に無効のフラグを立て、ボタンは無効のままになります。
次に、p.matcher()関数を使用してマッチャーインスタンスを宣言します。次に、マッチャー関数の入力を解析し、m.matches()を返します。入力がパターンに一致し、ボタンが有効になっている場合、結果はtrueを返し、一致しない場合はfalseを返します。
ifステートメントに戻ると、ボタンの条件がtrueのままの場合、ボタンを有効にする必要があります。条件がfalseに変更された場合のelseステートメントもあります。「番号が無効です」という通知をスローするエラーメッセージを設定しました。
この後、目的のエミュレーターでアプリを実行してテストできます。
RegExの汎用性により、多くのプログラミング言語で不可欠なツールになり、パターンをチェックする機能により、検証のための効率的なツールになりました。
汎用性の点でのその利点には、複数の欠点もあります。たとえば、構文が複雑であるため、ベテランプログラマーでさえRegExがやや困難になります。
それでも、レパートリーにReGexの知識があることは絶対に有利です。いつの日か、開発スペースにいる限り、ReGexが役立つからです。
このストーリーは、もともとhttps://blog.logrocket.com/guide-regular-expression-kotlin/で公開されました
1654945800
This is the kotlin implementation of Elrond SDK
This project was primarily designed for Android but is also compatible with any Kotlin-friendly app since it doesn't use the Android SDK
This SDK is built with the clean architecture principles.
Interaction are done through usecases
Here is an example for sending a transaction.
// Create a wallet from mnemonics
val wallet = Wallet.createFromMnemonic(..., 0)
// Get information related to this address (ie: balance and nonce)
val account = ErdSdk.getAccountUsecase().execute(Address.fromHex(wallet.publicKeyHex))
// Get the network informations
val networkConfig = ErdSdk.getNetworkConfigUsecase().execute()
// Create the transaction object
val transaction = Transaction(
sender = account.address,
receiver = Address.fromHex(...),
value = 1000000000000000000.toBigInteger(), // 1 xEGLD
data = "Elrond rocks !",
chainID = networkConfig.chainID,
gasPrice = networkConfig.minGasPrice,
gasLimit = networkConfig.minGasLimit,
nonce = account.nonce
)
// Send transaction.
// Signature is handled internally
val sentTransaction = ErdSdk.sendTransactionUsecase().execute(transaction, wallet)
Log.d("Transaction", "tx:${sentTransaction.txHash}")
In a real world example, the usescases would be injected
The sample application showcase how to do it on Android with Hilt framework (see the Sample App).
API
Usecase | Endpoint |
---|---|
GetAccountUsecase | GET address/:bech32Address |
GetAddressBalanceUsecase | GET address/:bech32Address/balance |
GetAddressNonceUsecase | GET address/:bech32Address/nonce |
GetAddressTransactionsUsecase | GET address/:bech32Address/transactions |
GetTransactionInfoUsecase | GET transaction/:txHash |
GetTransactionStatusUsecase | GET transaction/:txHash/status |
SendTransactionUsecase | POST transaction/send |
EstimateCostOfTransactionUsecase | POST transaction/cost |
GetNetworkConfigUsecase | GET network/config |
QueryContractUsecase | POST vm-values/query |
QueryContractHexUsecase | POST vm-values/hex |
QueryContractStringUsecase | POST vm-values/string |
QueryContractIntUsecase | POST vm-values/int |
Usecase | Description |
---|---|
CallContractUsecase | Interact with a Smart Contract (execute function): equivalent to erdpy contract call |
DNS
Usecase | Description |
---|---|
RegisterDnsUsecase | Send a register transaction to the appropriate DNS contract from given user and with given name: equivalent to erdpy dns register |
GetDnsRegistrationCostUsecase | Gets the registration cost from a DNS smart contract: equivalent to erdpy dns registration-cost |
CheckUsernameUsecase | Can be useful for validating a text field before calling RegisterDnsUsecase |
// default value is ProviderUrl.DevNet
ErdSdk.setNetwork(ProviderUrl.MainNet)
// configure the OkHttpClient
ErdSdk.elrondHttpClientBuilder.apply {
addInterceptor(HttpLoggingInterceptor())
}
The SDK is not yet uploaded to a maven repository
You can build the jar from the sources by running mvn package
For a complete example you can checkout this sample application
Download Details:
Author: ElrondNetwork
Source Code: https://github.com/ElrondNetwork/elrond-sdk-erdkotlin
License: GPL-3.0 license
1654642320
WavesSDK is a collection of libraries used to integrate Waves blockchain features into your Android application
Waves is an open source blockchain platform.
You can use it to build your own decentralised applications. Waves provides full blockchain ecosystem including smart contracts language called RIDE.
There is a huge collection of nodes deloyed by miners that store all of the network information in the chain of blocks (aka blockchain), process requests and can add new transactions to the network after checking their compliance with the rules. The miners are rewarded with the network coins called MRT.
The main advantage of this technology is that each node is a synchronized copy of the main blockchain: it means that the information is stored decentralized and won't be overwritten globally if one of the users changes it at one of the node storages. This can garantee that the user's information will stay fair and unchangable.
The important addition is that the service built using Waves blockchain looks like a usual web application and doesn't make user experience more difficult.
You can read the Waves node description and the definitions page for a better understanding of the blockchain functionality.
To build your first Waves platform integrated application and start using all of the blockchain features please go directly to the Waves Android SDK QuickStart tutorial and follow the instructions.
There are three main SDK services that provide the blockchain interactions:
To test your app you can use Testnet. This is a Waves Mainnet duplicate where it's possible to repeat the real accounts structure without spending paid WAVES tokens. You can create multiple accounts, top up their balances using Faucet (just insert the account address to the input field and get 10 test tokens) and deploy RIDE scripts (as known as "smart contracts" or "dApps") using Waves RIDE IDE.
Keep up with the latest news and articles, and find out all about events happening on the Waves Platform.
Download Details:
Author: wavesplatform
Source Code: https://github.com/wavesplatform/WavesSDK-android
License: MIT license
1654347600
Use this library to efficiently download and upload data in React Native. The library was inspired by rn-fetch-blob, and is focused strictly on http file transfers.
Install using yarn
yarn add react-native-blob-courier
Or install using npm
npm install react-native-blob-courier
Note: you may have success with earlier versions of React Native but these are neither tested nor supported.
The library provides both a fluent and a more concise interface. In the examples the concise approach is applied; fluent interface is demonstrated later in this document.
import BlobCourier from 'react-native-blob-courier';
// ...
// Download a file
const request0 = {
filename: '5MB.zip',
method: 'GET',
mimeType: 'application/zip',
url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};
const fetchedResult = await BlobCourier.fetchBlob(request0);
console.log(fetchedResult);
// {
// "data": {
// "absoluteFilePath": "/path/to/app/cache/5MB.zip",
// "response": {
// "code":200,
// "headers": {
// "some_header": "some_value",
// ...
// }
// },
// },
// "type":"Unmanaged"
// }
// ...
// Upload a file
const absoluteFilePath = fetchedResult.data.absoluteFilePath;
const request1 = {
absoluteFilePath,
method: 'POST',
mimeType: 'application/zip',
url: 'https://file.io',
};
const uploadResult = await BlobCourier.uploadBlob(request1);
console.log(uploadResult):
// {
// "response": {
// "code": {
// "data": "<some response>",
// "headers": {
// "some_header": "some_value",
// ...
// }
// }
// }
// Multipart file upload
const absoluteFilePath = fetchedResult.data.absoluteFilePath;
const request2 = {
method: 'POST',
parts: {
body: {
payload: 'some_value',
type: 'string',
},
file: {
payload: {
absoluteFilePath,
mimeType: 'application/zip',
},
type: 'file',
},
},
url: 'https://file.io',
};
const multipartUploadResult = await BlobCourier.uploadBlob(request1);
console.log(multipartUploadResult):
// {
// "response": {
// "code": {
// "data": "<some response>",
// "headers": {
// "some_header": "some_value",
// ...
// }
// }
// }
import BlobCourier from 'react-native-blob-courier';
// ...
// Download a file
const request0 = {
// ...
onProgress: ((e: BlobProgressEvent) => {
console.log(e)
// {
// "written": <some_number_of_bytes_written>,
// "total": <some_total_number_of_bytes>
// }
})
};
const fetchedResult = await BlobCourier.fetchBlob(request0);
// ...
// Upload a file
const request1 = {
// ...
onProgress: ((e: BlobProgressEvent) => {
console.log(e)
// {
// "written": <some_number_of_bytes_written>,
// "total": <some_total_number_of_bytes>
// }
})
};
const uploadResult = await BlobCourier.uploadBlob(request1)
// ...
// Set progress updater interval
const request2 = ...
const someResult =
await BlobCourier
.fetchBlob({
...request2,
progressIntervalMilliseconds: 1000,
});
import BlobCourier from 'react-native-blob-courier';
// ...
const request = {
android: {
useDownloadManager: true // <--- set useDownloadManager to "true"
},
filename: '5MB.zip',
method: 'GET',
mimeType: 'application/zip',
url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};
const fetchResult = await BlobCourier.fetchBlob(request);
console.log(fetchedResult);
// {
// "data": {
// "result": "SUCCESS",
// "absoluteFilePath": "/path/to/app/cache/5MB.zip"
// },
// "type":"Managed"
// }
Sometimes order of multipart fields matters, and Blob Courier respects the order in which parts are provided. There is a catch though: due to how JavaScript works, when object keys are regular strings they are kept in the order they were added unless the keys are strings containing numbers, e.g.:
Object.keys({
"b": "some_value1",
"c": "some_value2",
"a": "some_value3",
})
// ['b', 'c', 'a']
Object.keys({
"b": "some_value1",
"c": "some_value2",
"a": "some_value3",
"3": "some_value4",
"2": "some_value5",
"1": "some_value6",
});
// ['1', '2', '3', 'b', 'c', 'a']
The way to work around this, is to wrap all keys in a Symbol
, by using Symbol.for
. Do not use Symbol(<value>)
, this will not work, e.g.:
Object.getOwnPropertySymbols({
[Symbol.for("b")]: "some_value1",
[Symbol.for("c")]: "some_value2",
[Symbol.for("a")]: "some_value3",
[Symbol.for("3")]: "some_value4",
[Symbol.for("2")]: "some_value5",
[Symbol.for("1")]: "some_value6",
});
// [Symbol('b'), Symbol('c'), Symbol('a'), Symbol('3'), Symbol('2'), Symbol('1')]
import BlobCourier from 'react-native-blob-courier';
// ...
const abortController = new AbortController();
const { signal } = abortController;
const request0 = {
// ...
signal,
};
try {
BlobCourier.fetchBlob(request0);
abortController.abort();
} catch (e) {
if (e.code === ERROR_CANCELED_EXCEPTION) {
// ...
}
}
// ...
Blob Courier provides a fluent interface, that both protects you from using impossible setting combinations and arguably improves readability.
const req0 = ...
const someResult =
await BlobCourier
.settings({
progressIntervalMilliseconds: 1000,
})
.onProgress((e: BlobProgressEvent) => {
// ...
})
.useDownloadManagerOnAndroid({
description: "Some file description",
enableNotification: true,
title: "Some title"
})
.fetchBlob(req0)
fetchBlob(input: BlobFetchRequest)
Required
Field | Type | Description |
---|---|---|
filename | string | The name the file will have on disk after fetch. |
mimeType | string | What is the mime type of the blob being transferred? |
url | string | From which url will the blob be fetched? |
Optional
Field | Type | Description | Default |
---|---|---|---|
android | AndroidSettings | Settings to be used on Android | { downloadManager: {}, target: 'cache', useDownloadManager: false } |
headers | { [key: string]: string } | Map of headers to send with the request | {} |
ios | IOSSettings | Settings to be used on iOS | { target: 'cache' } |
headers | { [key: string]: string } | Map of headers to send with the request | {} |
method | string | Representing the HTTP method | GET |
onProgress | (e: BlobProgressEvent) => void | Function handling progress updates | () => { } |
signal | AbortSignal | Request cancellation manager | null |
Response
Field | Type | Description |
---|---|---|
type | "Managed" | "Unmanaged" | Was the blob downloaded through Android Download Manager, or without? |
data | BlobManagedData | BlobUnmanagedData | Either managed or HTTP response data |
uploadBlob(input: BlobUploadRequest)
Alias for:
const someResult =
await BlobCourier
// ...
.uploadParts({
headers,
method,
parts: {
file:
payload: {
absoluteFilePath,
filename,
mimeType,
},
type: 'file',
},
},
returnResponse,
url,
})
Required
Field | Type | Description |
---|---|---|
absoluteFilePath | string | Path to the file to be uploaded |
mimeType | string | Mime type of the blob being transferred |
url | string | Url to upload the blob to |
Optional
Field | Type | Description | Default |
---|---|---|---|
filename | string | Map of headers to send with the request | <name part of 'absoluteFilePath'> |
headers | { [key: string]: string } | Map of headers to send with the request | {} |
method | string | The HTTP method to be used in the request | "POST" |
multipartName | string | Name for the file multipart | "file" |
onProgress | (e: BlobProgressEvent) => void | Function handling progress updates | () => { } |
returnResponse | boolean | Return the HTTP response body? | false |
signal | AbortSignal | Request cancellation manager | null |
uploadParts(input: BlobMultipartUploadRequest)
Required
Field | Type | Description |
---|---|---|
parts | { [key: string]: BlobMultipart } | The parts to be sent |
url | string | Url to upload the blob to |
Optional
Field | Type | Description | Default |
---|---|---|---|
headers | { [key: string]: string } | Map of headers to send with the request | {} |
method | string | The HTTP method to be used in the request | "POST" |
onProgress | (e: BlobProgressEvent) => void | Function handling progress updates | () => { } |
returnResponse | boolean | Return the HTTP response body? | false |
signal | AbortSignal | Request cancellation manager | null |
Response
Field | Type | Description |
---|---|---|
response | BlobUnmanagedHttpResponse | The HTTP response |
AndroidDownloadManagerSettings
Field | Type | Description |
---|---|---|
description? | string | Description of the downloaded file |
enableNotification? | boolean | Display notification when download completes |
title? | string | Title to be displayed with the download |
AndroidSettings
Field | Type | Description |
---|---|---|
downloadManager | AndroidDownloadManagerSettings | Settings to be used on download manager |
target | "cache" | "data" | Where will the file be stored? |
useDownloadManager | boolean | Enable download manager on Android? |
BlobManagedData
Field | Type | Description |
---|---|---|
absoluteFilePath | string | The absolute file path to where the file was stored |
result | "SUCCESS" | "FAILURE" | Was the request successful or did it fail? |
BlobMultipart
Required
Field | Type | Description |
---|---|---|
payload | BlobMultipartFormData | BlobMultipartFormDataFile | Contains the payload of the part |
type | "string" | "file" | What is the type of the payload? |
BlobMultipartFormData
Type of string | { [key:string] : any }
BlobMultipartFormDataFile
Required
Field | Type | Description |
---|---|---|
absoluteFilePath | string | Path to the file to be uploaded |
mimeType | string | Mime type of the blob being transferred |
Optional
Field | Type | Description | Default |
---|---|---|---|
filename | string | Map of headers to send with the request | <name part of 'absoluteFilePath'> |
BlobProgressEvent
Field | Type | Description |
---|---|---|
written | number | Number of bytes processed |
total | number | Total number of bytes to be processed |
BlobUnmanagedData
Field | Type | Description |
---|---|---|
absoluteFilePath | string | The absolute file path to where the file was stored |
response | BlobUnmanagedHttpResponse | HTTP response, including headers and status code |
BlobUnmanagedHttpResponse
Field | Type | Description |
---|---|---|
code | number | HTTP status code |
headers | { [key: string]: string } | HTTP response headers |
IOSSettings
Field | Type | Description |
---|---|---|
target | "cache" | "data" | Where will the file be stored? |
You can find an example of how to use the library in the example directory.
Add the following line to AndroidManifest.xml
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" (...)>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
(...)
<application (...)>
<activity (...)>
<intent-filter>
(...)
+ <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
(...)
Grant permissions using the PermissionAndroid API, like so:
const function App = () => {
// ...
React.useEffect(() => {
const requestPermissionAsync = async () => {
try {
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
);
// ...
} catch (err) {
console.error(err);
}
// ...
};
requestPermissionAsync();
}, []);
// ...
Add to Info.plist
of your app:
<key>NSAllowsArbitraryLoads</key>
<true/>
This library allows you to use the integrated download manager on Android, this option is not available for iOS.
To enable the download manager, simply set the request's useDownloadManager
property of field android
to true
when passing it to fetchBlob
, or call the useDownloadManagerOnAndroid
method when using the fluent interface.
As this library is focussed on transferring files, it only supports storage to the app's cache and data directories. To move files from these app specific directories to other locations on the filesystem, use another library like @react-native-community/cameraroll, e.g.:
import BlobCourier from 'react-native-blob-courier';
import CameraRoll from '@react-native-community/cameraroll';
// ...
const request = {
filename: 'teh_cage640x360.png',
method: 'GET',
mimeType: 'image/png',
url: 'https://www.placecage.com/640/360',
};
const cageResult = await BlobCourier.fetchBlob(request)
const cageLocalPath = cageResult.data.absoluteFilePath
CameraRoll.save(cageLocalPath);
See the contributing guide to learn how to contribute to the repository and the development workflow.
Download Details:
Author: edeckers
Source Code: https://github.com/edeckers/react-native-blob-courier
License: MPL-2.0 license
#react #reactnative #mobileapp #javascript #backend #kotlin #typescript