Search for:

Android Arsenal – Bilddialer

Unterstützung für neue Versionen von Android

Wir fangen an

Sucht hinzufügen

    implementation 'ro.dragossusi.android:imagepicker:1.0.0'

Geben Sie Ihr Zielverzeichnis an

Sie können Ihr Zielverzeichnis mit einer Code- oder Zeichenfolgenressource deklarieren.

Initialisieren Sie für Code das ImagePicker-Verzeichnis (normalerweise in der Application-Klasse):

ImagePicker.directory = "YourDirectory"

Für String-Ressource:

<string name="image_picker_folder_name">YourDirectory</string>

Erstellen Sie einen ImageWrapper

Die Bibliothek wird mit einem Cover für Fragmente geliefert:

    private val imagePickWrapper = FragmentImagePickerWrapper(
        this,
        "${BuildConfig.APPLICATION_ID}.filesystem.provider",
        ::onPermissionError
    ) { uri:Uri?->
        //todo what you want with the image
    }

    private fun onPermissionError(permission: String) {
        //todo permission error handling
        AlertDialog.Builder(requireContext())
            .setTitle("Your permission error")
            .setMessage(permission)
            .show()
    }

Verwenden Sie die integrierte AvatarImageView

Fügen Sie AvatarImageView zum XML-Layout hinzu

<ro.dragossusi.android.imagepicker.ui.AvatarImageView
    android:id="@+id/image_avatar"
    android:layout_width="120dp"
    android:layout_height="120dp"
    android:background="?selectableItemBackgroundBorderless"
    android:clickable="true"
    android:focusable="true"
    tools:src="@tools:sample/avatars" />

Legen Sie die Hülle sichtbar

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //auto sets onClickListener
        findViewById<AvatarImageView>(R.id.image_avatar)
            .imagePickerWrapper = imagePickWrapper
    }

Android Arsenal – Validierung

Dieses Projekt bietet eine einfache und vereinfachte Möglichkeit, TextInputLayoutEditText zu validieren

Installation

  1. Fügen Sie das JitPack-Repository zu Ihrer Build-Datei hinzu
 allprojects {
  repositories {
   ...
   maven { url 'https://jitpack.io' }
  }
 }
  1. Sucht hinzufügen
    dependencies {
         implementation 'com.github.BrianHardyR:TextInputLayoutValidator:1.1.2'
 }

Wie

Einfache Validierung

val myinput = findViewById<TextInputLayout>(R.id.my_input)

myinput.validate(
    default = "my default text",
    validators = listOf(
        { text -> t.isNotEmpty() },
        ...
    ),
    error = "my error text",
    onValid = { validText -> 
        // do something with the valid text
     }
)

Verwendung des Validierungsobjekts

Fehlermeldung für jede Validierungsbedingung
val phoneNumberValidator = TextInputValidator(
    defaultString = "My default text",
    validators = listOf(
        { text -> (text.length > 12) to "Length must be greater than 12"},
        { text -> text.startsWith('+') to "Input must start with '+'"},
        ...
    )
)
Eine Fehlermeldung
val phoneNumberValidator = TextInputValidationObj(
    default = "My default text",
    error = "Please enter a valid phone number",
    validators = listOf(
        { text -> text.length > 12},
        { text -> text.startsWith('+')},
        ...
    )
)

myinput.validate(phoneNumberValidator){ validText ->
    // do something with the valid text
}

Eingabe bestätigen Nachdem Sie das Validierungsobjekt auf TextInputLayout gesetzt haben, können Sie es überall in Ihrem Code überprüfen

myinput.valid() // Return a Boolean.

Android Arsenal – Validierung

Easy-Checker ist eine sehr einfache Bibliothek zur Validierung von Einträgen für Android, die in Kotlina geschrieben und auch über Java-Code leicht zugänglich ist. Sie erhalten viele Validierungsmethoden oder Bibliotheken. Aber die meisten von ihnen sind für diese einfache Aufgabe nicht so einfach, also suchen wir die meiste Zeit nach einer Legacy-If-Else-Kette, um alle Eingabefelder zu überprüfen, und wir alle wissen, wie nervig das ist.

  • Leeres Eingabefeld (Edittext)
  • Bestätigung der Telefonnummer
  • E-Mail-Validierung
  • Passwort Bestätigung
  • Bestätigen Sie die Passwortvalidierung
  • Benutzerdefinierter Validator
  • Varargs zur einfachen Eingabe von Eingabefeldern anstelle von Strings oder Listen
  • Benutzerdefinierte RegEx-Passwortvalidierung
  • Singleton-Klasse zum Entfernen von Objekten und Speicherlecks

Installation

Easy-Checker ermöglicht eine einfache Maven-basierte Installation

Schritt 1:

fügen Sie dies zu Ihrer build.gradle-Stammdatei hinzu

 allprojects {
 repositories {
 maven { url 'https://jitpack.io' }
 }
 }
Schritt 2:

Fügen Sie diese Abhängigkeit der Datei build.gradle auf Anwendungsebene hinzu

implementation 'com.github.mkhan9047:Easy-Checker:1.0.2'

Wie benutzt man?

Die Hauptklasse ist EasyChecker, es ist eine Singleton-Klasse, die nur eine öffentliche Funktion hat, validateInput (), es ist ein supereinfacher Prozess, fügen Sie Tags zum Eingabefeld hinzu, rufen Sie die Funktion mit möglichen Parametern auf!

Bitte fügen Sie android hinzu: tag = “Ihr Feldname” in jedem Eingabefeld (EditText oder TextView), Super einfach, Diese Bibliothek verwendet Tags, um die eigentliche Fehlermeldung auszuwerfen, z. B. “Name darf nicht leer sein” oder “E-Mail ist ungültig”. !
Tipp: Konstante Tags sind “Passwort”, “Telefon”, “E-Mail”, “Passwort bestätigen”. Wenn Sie diese Felder haben, verpassen Sie diese Tags nicht und ändern Sie auch nicht die Tag-Nachricht. Für andere Felder können Sie alles verwenden, was Sie wollen !
   <EditText
        android:id="@+id/firstEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Name"
        android:tag="Name"/>
        
        <EditText
        android:id="@+id/fourthEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Confirm Password"
        android:tag="Confirm Password">

Parameter

  • Kontext
  • Gewünschte Passwortlänge (Ganzzahl, standardmäßig Bereich 4 bis 24)
  • Beispielpasswort für die Passwortüberprüfung mit Regex, ich habe eine benutzerdefinierte Klasse mit einigen regulären Mustern geschrieben, siehe Abschnitt Passwortmuster, wenn Sie kein Passwortmuster verwenden möchten, übergeben Sie null oder PasswordPattern.PASSWORD_PATTERN_NONE
  • Eingabefelder oder TextFields (EditText, TextView) [Pass as much you have one by one It’s varargs]
      try {
              var isValidationSuccess = EasyChecker.validateInput(
                    MainActivity.this,
                    8,
                    PasswordPattern.PASSWORD_PATTERN_NONE,
                    firstNameEditText,
                    lastNameEditText,
                    emailEditText,
                    passwordEditText,
                    confrimPasswordEditText
                )
                /**you can either use the returned boolean or just do the implementaiton 
                here as errors are only thrown in exception blocks
                **/
               doSignUp()
                //or
                if(isValidationSuccess){
                    //if validated
                         doSignUp()
                }else{
                    //if not validated
                }
            } catch (developerErrorException: DeveloperErrorException) {
                //best approach to print in log
                developerErrorException.printStackTrace()
            } catch (inputErrorException: InputErrorException) {
                //best approach is show this to user
                Toast.makeText(
                    this,
                    inputErrorException.message,
                    Toast.LENGTH_LONG
                ).show()
            }

Ausnahmen

Es gibt zwei Arten von Ausnahmen, eine ist DeveloperException und die andere ist InputErrorException. Diese Ausnahmen sind im Grunde verifizierte Ausnahmen, also müssen Sie beide Ausnahmen abfangen.

  • DeveloperException: Grundsätzlich entfällt diese Ausnahme, wenn der Entwickler bei der Implementierung dieser Bibliothek einen Fehler gemacht hat, z. B. das Hinzufügen von “Tag” zum Eingabefeld oder Textfeld oder das Angeben falscher Parameter oder anderes

  • InputErrorException: Diese Ausnahme ist der Hauptvalidierungsfehler des Benutzers. Sie können die eigentliche Nachricht aus der Ausnahme abrufen und dem Benutzer anzeigen. Wenn beispielsweise ein Benutzer kein E-Mail-Feld eingegeben hat, würde er Folgendes erhalten: “E-Mail Feld darf nicht leer sein ! ” oder was auch immer du willst

Passwortmuster

Ich habe eine Klasse mit einer beliebten Regex-Passwortvorlage geschrieben, hier sind die Konstanten:

  • PasswordPattern.PASSWORD_PATTERN_NONE = Verwenden Sie dies, wenn Sie Regex nicht für die Passwortvalidierung verwenden möchten
  • PasswordPattern.PASSWORD_PATTERN_ONE = Mindestens acht Zeichen, mindestens ein Buchstabe und eine Zahl
  • PasswordPattern.PASSWORD_PATTERN_TWO = Mindestens acht Zeichen, mindestens ein Buchstabe, eine Zahl und ein Sonderzeichen
  • PasswordPattern.PASSWORD_PATTERN_THREE = Mindestens acht Zeichen, mindestens ein Großbuchstabe, ein Kleinbuchstabe und eine Zahl
  • PasswordPattern.PASSWORD_PATTERN_THREE = Mindestens acht Zeichen, mindestens ein Großbuchstabe, ein Kleinbuchstabe, eine Zahl und ein Sonderzeichen

Entwicklung

Möchten Sie einen Beitrag leisten? Toll! Stellen Sie einen Antrag auf Rückzug und geben Sie Ihr Bestes, ich werde bezahlen, wenn es für andere gut ist!

Lizenz

MIT-Lizenz Copyright (c) 2019 Mujahid Khan Hiermit wird jeder Person, die eine Kopie dieser Software und zugehöriger Dokumentationsdateien (die „Software“) erhält, kostenlos die Erlaubnis erteilt, mit der Software ohne Einschränkung, einschließlich ohne Einschränkung, Geschäfte zu tätigen das Recht, Kopien der Software zu verwenden, zu kopieren, zu modifizieren, zusammenzuführen, zu veröffentlichen, zu verteilen, unterzulizenzieren und/oder Kopien der Software zu verkaufen und die Personen, denen die Software zur Verfügung gestellt wurde, dazu zu ermächtigen, vorbehaltlich der folgenden Bedingungen: Teile der Software.

DIE SOFTWARE WIRD OHNE JEGLICHE AUSDRÜCKLICHE ODER STILLSCHWEIGENDE GEWÄHRLEISTUNG BEREITGESTELLT, EINSCHLIESSLICH, ABER NICHT BESCHRÄNKT AUF DIE VERKAUFSGARANTIE, EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. DIE AUTOREN ODER URHEBERRECHTSVERANTWORTLICHEN HAFTEN UNTER KEINEN UMSTÄNDEN FÜR JEGLICHE ANFRAGE, SCHÄDEN ODER SONSTIGE VERANTWORTUNG, OB IN DER ARBEIT DES VERTRAGES, DER TEILUNG ODER ANDERER, DIE HERVORKOMMEN

Android Arsenal – Scroll-Ansichten

Swiper für Android Jetpack Compose

Android Jetpack Compose Drag-Bibliothek.

Herunterladen

Gradl

Fügen Sie es zuerst Ihrem Root-Projekt hinzu build.gradle

allprojects {
    repositories {
        mavenCentral()
    }
}

Andere Add Sucht

dependencies {
    implementation 'io.github.lhoyong:swiper:1.0.1'
}

Einfaches Beispiel

Ziehen Sie die folgenden Elemente einfach mit der Kompilierungsfunktion.

Swiper(
        items = items,
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Gray),
        onSwiped = { /* TODO */ }, // swipe end callback
     ) { item ->
        // composable swipe item
        SwipeItem(item)
    }  

Gehen Sie einfach Beispiel.

Lizenz

Copyright 2021 by lhoyong

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Android Arsenal – Architektur

🌳 Mit dem ViewModel-Lebenszyklus können Sie Änderungen im Lebenszyklus des Jetpack-ViewModel überwachen und beobachten.
Es unterstützt auch nützliche Erweiterungsfunktionen für RxKotlin / RxJava und Korutine.

Einschließlich Ihres Projekts

Gradl

Fügen Sie den unten stehenden Code zu Ihrem eigenen hinzu Wurzel build.gradle Datei (nicht Ihr build.gradle-Dateimodul):

allprojects {
    repositories {
        mavenCentral()
    }
}

Fügen Sie dann die unten stehende Abhängigkeit zu Ihrer eigenen hinzu Modul‘S build.gradle Datei:

dependencies {
    implementation "com.github.skydoves:viewmodel-lifecycle:1.1.0"
}

SCHNAPPSCHUSS


Snapshots der aktuellen Entwicklungsversion von ViewModel-Lifecycle, die folgen, sind verfügbar die neuesten Versionen.

repositories {
   maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

Benutzen

ViewModel-Lifecycle ermöglicht es Ihnen, zwei Lebenszyklusänderungen zu beobachten: initialisiert ich gereinigt.

ViewModelLifecycleOwner

ViewModelLifecycleOwner ist der Besitzer eines erweiterbaren Lebenszyklus für das Jetpack ViewModel LifecycleOwner. Es überwacht und stellt Lebenszykluszustände für ViewModels bereit. Du kannst es bekommen ViewModelLifecycleOwner aus Ihrem ViewModel wie folgt:

class MyActivity : AppCompatActivity() {

  private val viewModel by viewModels<MyViewModel>()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    val viewModelLifecycleOwner = viewModel.viewModelLifecycleOwner
...

Sie können es auch wie folgt direkt in Ihr ViewModel übertragen:

class MyViewModel : ViewModel() {

  val lifecycleOwner = viewModelLifecycleOwner
}

ViewModelLifecycleOwner für LiveData

Sie können es auch verwenden, um Ihre eigenen zu beobachten Lebensdaten mit ViewModelLifecycleOwner gemäß dem ViewModel-Lebenszyklus. Wenn der Lebenszyklus in den gelöschten Zustand versetzt wird, wird der Beobachter automatisch entfernt.

class MyViewModel : ViewModel() {

  private val liveData = MutableLiveData<String>()

  init {
    val lifecycleOwner = liveData.observe(viewModelLifecycleOwner) {
      // sometihng
    }
  }
}

Hinweis: Wenn Sie verwenden ViewModelLifecycleOwner Um Ihre LiveData zu beobachten, erhalten Beobachter jedes Ereignis, bevor der Lebenszyklus in den gelöschten Zustand übergeht. Sie erhalten jedoch keine weiteren Ereignisse durch Freizeitaktivitäten wie Bildschirmdrehung. Stellen Sie also sicher, welche lifecycleOwner ist die beste Lösung.

ViewModelLifecycle

ViewModelLifecycle ist die Umsetzung Lebenszyklusdie dem Lebenszyklus des ViewModel folgt. ViewModelLifecycle mehrfach verarbeitet LifecycleObserver wie zum Beispiel ViewModelLifecycleObserver um den ViewModel-Lebenszyklus zu überwachen. ViewModelLifecycle gehört ViewModelLifecycleOwnerund Sie können es direkt von erhalten [ViewModelLifecycleOwner] folgendermaßen:

val viewModelLifecycle = viewModelLifecycleOwner.viewModelLifecycle

ViewModelLifecycle Observer

Sie können Änderungen im Lebenszyklus nachverfolgen ViewModelLifecycle mit addViewModelLifecycleObserver Verlängerung wie folgt:

viewModel.viewModelLifecycleOwner.addViewModelLifecycleObserver { viewModelState ->
  when (viewModelState) {
    ViewModelState.INITIALIZED -> // viewModel was initialized
    ViewModelState.CLEARED -> // viewModel was cleraed
  }
}

Sie können auch Änderungen im Lebenszyklus beobachten ViewModelLifecycle mit addObserver folgendermaßen:

viewModelLifecycleOwner.lifecycle.addObserver(
  object : DefaultViewModelLifecycleObserver {
    override fun onInitialized(viewModelLifecycleOwner: ViewModelLifecycleOwner) {
        // viewModel was initialized
    }

    override fun onCleared(viewModelLifecycleOwner: ViewModelLifecycleOwner) {
        // viewModel was cleraed
    }
  }
)

Sie können auch Ihre eigenen benutzerdefinierten Lifecycle Observer-Klassen implementieren DefaultViewModelLifecycleObserver ich FullViewModelLifecycleObserver Schnittstellen.

ViewModel-Lebenszyklus für RxKotlin (RxJava)

ViewModel Lifecycle bietet nützliche Erweiterungen für RxKotlin (RxJava).

Gradl

Fügen Sie die Sucht unten Ihrer eigenen hinzu Modul build.gradle Datei:

dependencies {
    // RxKotlin3 (RxJava3)
    implementation "com.github.skydoves:viewmodel-lifecycle-rxkotlin3:$version"

    // RxKotlin2 (RxJava2)
    implementation "com.github.skydoves:viewmodel-lifecycle-rxkotlin2:$version"
}

AutoDisposable

Mit autoDisposable Erweiterung können Sie eine erstellen Disposable Delegieren Sie die anzurufende Eigenschaft dispose() funktionieren automatisch, wenn das ViewModel wie folgt gelöscht wird:

class MyViewModel : ViewModel() {

  // dispose CompositeDisposable automatically when viewModel is getting cleared
  val compositeDisposable by autoDisposable(CompositeDisposable())
}

Der autoDisposable Die Erweiterung erstellt eine schreibgeschützte Eigenschaft, die sie empfängt Disposable Schnittstelle als Anfangswert.

ViewModel-Lebenszyklus für Volumenkörper

ViewModel-Lifecycle unterstützt auch Bits zum Verfolgen und Beobachten von ViewModel-Lebenszyklusänderungen.

Gradl

Fügen Sie die Sucht unten Ihrer eigenen hinzu Modul build.gradle Datei:

dependencies {
    implementation "com.github.skydoves:viewmodel-lifecycle-coroutines:$version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
}

ViewModelLifecycle-Ablauf

Sie können Lebenszyklusänderungen als Flow with beobachten viewModelLifecycleFlow Verlängerung wie folgt:

class MyInteractor(
  private val viewModelLifecycleOwner: ViewModelLifecycleOwner
) : CoroutineScope {

  override val coroutineContext: CoroutineContext = SupervisorJob() + Dispatchers.Main

  init {
    launch(coroutineContext) {
      viewModelLifecycleOwner.viewModelLifecycleFlow().collect { viewModelState ->
        when (viewModelState) {
          ViewModelState.INITIALIZED -> // ViewModel was initialized.
          ViewModelState.CLEARED -> {
            // ViewModel was cleared.
            coroutineContext.cancel() // cancel the custom scope.
          }
        }
      }
    }
  }
}

Stellen Sie sicher, dass Sie Ihre Gewohnheit stornieren CoroutineScope nach Beobachtung ViewModelState.CLEAREDich die viewModelLifecycleFlow Erweiterung muss laufen hauptsächlich Faden.

Ist diese Bibliothek nützlich für Sie? ❤️

Unterstützen Sie dies durch Ihren Beitritt Sterngucker für dieses Depot.
ich Folgen mich für meine nächsten Kreationen! 🤩

Copyright 2022 skydoves (Jaewoong Eum)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the L

Android Arsenal – Navigation

Eine kleine und einfache, aber dennoch ausgereifte und anpassbare Navigationsbibliothek für Jetpack komponieren:

  • Voll Typsicherheit
  • Staatlicher Wiederaufbau
  • Verschachtelte Navigation mit unabhängigen Backstacks
  • Eigener Lebenszyklus, gespeicherter Zustand und Anzeigemodelle für jeden Backstack-Eintrag
  • Animierte Übergänge
  • Die Navigationslogik kann einfach in die ViewModel-Schicht verschoben werden
  • Keine Builder, keine obligatorischen Superklassen für Ihre Verbundwerkstoffe
  • Kann verwendet werden, um Dialoge zu verwalten

Wir fangen an

Fügen Sie Ihrem Projekt eine Abhängigkeit hinzu:

implementation("dev.olshevski.navigation:reimagined:1.0.0-beta01")

Definieren Sie beispielsweise eine Reihe von Bildschirmen als versiegelte Klasse:

sealed class Screen : Parcelable {

    @Parcelize
    object First : Screen()

    @Parcelize
    data class Second(val id: Int) : Screen()

    @Parcelize
    data class Third(val text: String) : Screen()

}

Erstellen Sie komponiert mit NavController ich NavHost:

@Composable
fun NavHostScreen() {
    val navController = rememberNavController<Screen>(
        startDestination = Screen.First,
    )

    NavBackHandler(navController)

    NavHost(controller = navController) { screen ->
        when (screen) {
            Screen.First -> Column {
                Text("First screen")
                Button(onClick = {
                    navController.navigate(Screen.Second(id = 42))
                }) {
                    Text("To Second screen")
                }
            }
            is Screen.Second -> Column {
                Text("Second screen: ${screen.id}")
                Button(onClick = {
                    navController.navigate(Screen.Third(text = "Hello"))
                }) {
                    Text("To Third screen")
                }
            }
            is Screen.Third -> {
                Text("Third screen: ${screen.text}")
            }
        }
    }
}

Wie du sehen kannst, NavController verwendet, um zwischen Bildschirmen zu wechseln, NavBackHandler übernimmt den Schub für den Rücken und NavHost Es bietet einfach eine Komposition, die dem neuesten Backstack-Ziel entspricht. So einfach.

Grundlagen

Hier ist der allgemeine Arbeitsablauf der Bibliothek:

Lassen Sie uns auf jeden von ihnen ins Detail gehen.

NavController

Dies ist der Hauptnavigationskontrollpunkt. Verfolgt alle aktuellen Backstack-Einträge und hält sie bei Prozessaktivitäten/Erneuerung fest.

NavController kann mit erstellt werden rememberNavController innerhalb der Komposition oder mit navController außerhalb davon. Letzteres kann verwendet werden, um den NavController im ViewModel zu speichern. Da es die Parcelable-Schnittstelle implementiert, kann (und sollte) es darin gespeichert werden SavedStateHandle.

Beide rememberNavController ich navController Methoden akzeptieren startDestination als Parameter. Wenn Sie einen NavController mit einer beliebigen Anzahl von Backstack-Elementen erstellen möchten, können Sie verwenden initialBackstack stattdessen Parameter.

Reiseziele

NavController akzeptiert alle zulässigen Typen als Ziele. Die Anforderungen sind:

  1. Der Typ muss entweder Parcelable oder Serializable oder Primitive oder ein beliebiger anderer Typ sein, der geschrieben werden kann Baugrundstück.

  2. Der Typ muss jeder sein Stabiloder Unveränderlichoder primitiv.

Navigationsmethoden

Es gibt mehrere vordefinierte Methoden, die für die grundlegende In-App-Navigation geeignet sind: navigate, pop, popUpTo, popAll, replaceLast, replaceUpTo, replaceAll. Sie sind alle ziemlich verständlich.

Wenn Ihr Anwendungsfall eine fortgeschrittene Backstack-Manipulation erfordert, können Sie es verwenden setNewBackstackEntries Methode. Tatsächlich ist dies die einzige öffentliche Methode, die in NavController definiert ist, alle anderen Methoden werden als Erweiterungen und Verwendung angegeben setNewBackstackEntries unter der Haube. Sie können sehen, wie die neue Expansionsmethode ist navigateToTab ist implementiert in Probe.

NavBackstack

Dies ist eine schreibgeschützte Klasse, mit der Sie auf aktuelle Backstack-Einträge und die neueste NavAction zugreifen können. Eigenschaften sind Backup MutableStateCompose wird also über Änderungen benachrichtigt.

Wenn Sie Backstack-Änderungen außerhalb der Komposition hören möchten, können Sie dies einstellen onBackstackChange Listener im NavController.

NavHost

NavHost ist ein Compiler, der den letzten Backstack-Eintrag anzeigt und alle Komponenten bereitstellt, die diesem bestimmten Eintrag zugeordnet sind: Lebenszyklus, SavedStateRegistry ich ViewModelStore. Alle diese Komponenten werden durch bereitgestellt CompositionLocalProvider innerhalb der entsprechenden Eigentümer LocalLifecycleOwner, LocalSavedStateRegistryOwner ich LocalViewModelStoreOwner.

Komponenten werden gespeichert, bis der zugeordnete Eintrag aus dem Backstack entfernt wird (oder bis der übergeordnete Eintrag, der den aktuellen Slave-NavHost enthält, entfernt wird).

NavHost selbst bietet keine animierten Übergänge, es springt einfach zum nächsten Ziel.

AnimierterNavHost

AnimatedNavHost enthält alle Funktionen eines regulären NavHost, unterstützt aber auch animierte Übergänge. Der Standardübergang ist ein einfaches Crossfade, aber Sie können jeden Übergang im Detail an Ihren eigenen anpassen AnimatedNavHostTransitionSpec Implementierung.

Hier ist eine mögliche Implementierung von AnimatedNavHostTranzitionSpec:

val CustomTransitionSpec = AnimatedNavHostTransitionSpec<Any?> { action, from, to ->
    val direction = if (action == NavAction.Pop) {
        AnimatedContentScope.SlideDirection.End
    } else {
        AnimatedContentScope.SlideDirection.Start
    }
    slideIntoContainer(direction) with slideOutOfContainer(direction)
}

Stellen Sie es auf AnimatedNavHost ein:

AnimatedNavHost(
    controller = navController,
    transitionSpec = CustomTransitionSpec
) { destination ->
    // ...
}

und am Ende sieht es so aus:

In AnimatedNavHostTransitionSpec erhalten Sie die Parameter:

  • action – ein Hinweis auf die neueste NavController-Methode, die den Backstack geändert hat
  • from – vorheriges sichtbares Ziel
  • to – Ziel sichtbares Ziel

Diese Informationen reichen aus, um Übergänge für jede mögliche Kombination von Bildschirm- und Navigationsaktionen auszuwählen.

NavAction

Es gibt vier Standardtypen von NavAction:

  • Pop, Replace ich Navigate – passende Einrichtungen pop…, replace…, navigate NavController-Methoden
  • Idle – die Standardaktion des neu erstellten NavControllers

Sie können auch einen neuen Aktionstyp erstellen, indem Sie die Zusammenfassung erweitern NavAction Klasse. Übertragen Sie diese neue Art auf setNewBackstackEntries NavController-Methode und verarbeiten Sie sie in AnimatedNavHostTransitionSpec.

Die letzte Aktion ist auch über erreichbar action im Besitz von NavBackstack.

DialogNavHost

Eine Version von NavHost, die besser für die Anzeige von Dialogen geeignet ist. Es basiert auf AnimatedNavHost und sorgt für einen fließenderen Übergang zwischen flimmerfreien Scrim-/Fade-Dialogen.

Wenn Sie sehen möchten, wie Sie die Navigation durch Dialoge implementieren können, erkunden Sie Probe.

Beachten Sie, dass DialogNavHost Ihre Komponenten nicht in Dialog umschließt. Sie müssen entweder Dialog oder AlertDialog verwenden, die innerhalb einer kompiliert werden können contentSelector ich selbst.

Handhabung im Hintergrund

Back-Handling in der Bibliothek ist Opt-in, nicht Opt-out. Weder NavController noch NavHost steuern allein das Drücken einer Zurück-Taste. Du kannst hinzufügen NavBackHandler oder gemein BackHandler um auf Schub nach hinten zu reagieren, wo es nötig ist.

NavBackHandler ist die grundlegendste Implementierung von Calling BackHandler pop bis ein Gegenstand im Stapel verbleibt. Es wird dann deaktiviert, sodass jeder übergeordnete BackHandler auf das Drücken der Zurück-Schaltfläche reagieren kann.

Wichtiger Hinweis: Stellen Sie immer Ihren NavBackHandler / BackHandler ein Vor passender NavHost. Lesen Sie die Erklärung Hier.

Verschachtelte Navigation

Das Hinzufügen einer verschachtelten Navigation ist so einfach wie das Platzieren eines NavHosts in einem anderen. Alles ist richtig und einfach gemacht.

Sie können so viele Schichten so tief gehen, wie Sie möchten. Es ist wie Fraktaleaber im Navi.

Wiederherstellen von Werten zu früheren Zielen

Da Zieltypen nicht unbedingt unveränderlich sein müssen, können Sie sie beim Backstacking ändern. Dies kann verwendet werden, um Werte von anderen Zielen wiederherzustellen. Machen Sie einfach eine veränderbare Eigenschaft mit einem Backup mutableStateOf und ändern Sie es nach Bedarf. Sie können die Demo sehen Hier.

Notiz: Im Allgemeinen macht die Rückgabe von Werten an das vorherige Ziel die Logik der Navigation komplexer. Außerdem garantiert dieser Ansatz nicht die Kompilierzeittypsicherheit. Verwenden Sie es vorsichtig und wenn Sie sicher sind, was Sie tun. Manchmal kann es einfacher sein, einen gemeinsamen Eigentümer der Bedingung zu verwenden.

Dokumentation und Probe

Erkunden Sie die KDoc-Bibliotheksdokumentation für weitere Details zu jeder Komponente und allen unterstützten Funktionen.

Recherchieren Sie auch eine Probe. Es bietet Demonstrationen aller oben genannten Funktionen und noch mehr. Das Beispiel zeigt:

  • verschachtelte Navigation
  • Tab-Navigation
  • Verwenden Sie NavHost / AnimatedNavHost
  • Dialoge
  • Weiterleitung und Wiederherstellung von Werten
  • ViewModels
  • Anheben des NavControllers auf die ViewModel-Schicht

Warum Beta

Ich bin sehr zufrieden mit der Form und Form der Bibliothek. Ich verbrachte lange schlaflose Nächte damit, alle Eckgehäuse zu debuggen und zu polieren.

Im Moment freue ich mich über Ihr Feedback und eine geringfügige Feinabstimmung der API (falls vorhanden). Sollten sich Änderungen ergeben, können Sie mit einem Hinweis in den Release Notes rechnen.

Oh

Ich habe lange über die Architektur von Android-Anwendungen und insbesondere über die Navigation nachgedacht. Als ich Compose kennenlernte, konnte ich endlich eine Navigationsstruktur erstellen, die alle meine Bedürfnisse perfekt erfüllt.

Gestalten in Form einer öffentlichen Bibliothek schließt für mich die Gestalt. Ich bin endlich fertig damit. Auf neue Projekte!

Wenn Ihnen diese Bibliothek gefällt und Sie sie nützlich finden, markieren Sie das Projekt und teilen Sie es mit Ihren Mitentwicklern. Ein bisschen Werbung schadet nie.

Android Arsenal – Dateisystem

Sie können diese leichtgewichtige Bibliothek verwenden, um die Anhangsfunktion zu implementieren (Bilder mit der Kamera aufnehmen, Dateien / Bilder aus einer Galerie oder einem Dateisystem oder einer Google-Festplatte herunterladen). Die Bibliothek hilft Ihnen, alle Dateiauswahlprozesse zu vereinfachen, ohne sich Gedanken über Systemberechtigungen machen zu müssen

Sprachunterstützung

Warnung!

  1. Diese Bibliothek wurde mit erstellt AndroidX.Also empfehle ich Ihnen, Ihr Projekt zu migrieren AndroidX Andernfalls kann es zu Problemen bei der gemeinsamen Verwendung von Androidx- und Libo-Unterstützung kommen.

  2. Sie können einem Fehler begegnen Invoke-customs are only supported starting with android 0 --min-api 26 .Um dies zu lösen, fügen Sie unterhalb der Zeile auf Anwendungsebene hinzu build.gradle Datei.

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
  1. Fügen Sie Berechtigungen und Anbieter hinzu AndroidManifest.xml
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
 <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.attachmentmanager"
            android:exported="false"
            android:grantUriPermissions="true"
            tools:replace="android:authorities">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider"
                tools:replace="android:resource" />
  </provider>
  1. Erstellen file_provider.xml in res/xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="myApp"
        path="Download/" />
    <external-files-path
        name="images"
        path="Pictures" />
</paths>
  1. Wenn Sie auf Android 11+ abzielen, müssen Sie die folgenden Abfragen hinzufügen AndroidManifest.xml
<queries>
        <intent>
            <action android:name="android.intent.action.OPEN_DOCUMENT" />
            <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
            <data android:mimeType="*/*" />
        </intent>
        <intent>
            <action android:name="android.intent.action.PICK" />
            <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
            <data android:mimeType="*/*" />
        </intent>
    </queries>
  
  1. Aktualisieren Sie die Projektebene build.gradle Datei.
allprojects {
   repositories {
       jcenter()
        maven { url "https://jitpack.io" }  //Make sure to add this in your project
   }
}
   implementation 'com.github.Zaid-Mirza:AttachmentManager:2.0.1'
  1. Führen Sie das AttachmentManager-Objekt mithilfe des Erstellungsformulars aus

Kotlin

private var attachmentManager: AttachmentManager? = null
var gallery = arrayOf("image/png",
            "image/jpg",
            "image/jpeg")
    var files = arrayOf("application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  // .ppt & .pptx
            "application/pdf")

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
      attachmentManager = AttachmentManager.AttachmentBuilder(this) // must pass Context
            .fragment(null) // pass fragment reference if you are in fragment
            .setUiTitle("Choose File") // title of dialog or bottom sheet
            .allowMultiple(false) // set true if you want make multiple selection, default is false
            .asBottomSheet(true) // set true if you need to show selection as bottom sheet, default is as Dialog
            .setOptionsTextColor(android.R.color.holo_green_light) // change text color
            .setImagesColor(R.color.colorAccent) // change icon color
            .hide(HideOption.DOCUMENT) // You can hide any option do you want
            .setMaxPhotoSize(200000) // Set max  photo size in bytes
            .galleryMimeTypes(gallery) // mime types for gallery
            .filesMimeTypes(files) // mime types for files
            .build(); // Hide any of the three options
       
    }
    

Java

    private AttachmentManager attachmentManager = null;
    String[] gallery = {"image/png",
            "image/jpg",
            "image/jpeg"};
    String[] files  = { "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .ppt & .pptx
            "application/pdf"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       attachmentManager = new AttachmentManager.AttachmentBuilder(this) // must pass Context
                .fragment(null) // pass fragment reference if you are in fragment
                .setUiTitle("Choose File") // title of dialog or bottom sheet
                .allowMultiple(false) // set true if you want make multiple selection, default is false
                .asBottomSheet(true) // set true if you need to show selection as bottom sheet, default is as Dialog
                .setOptionsTextColor(android.R.color.holo_green_light) // change text color
                .setImagesColor(R.color.colorAccent) // change icon color
                .hide(HideOption.DOCUMENT) // You can hide any option do you want
                .setMaxPhotoSize(200000) // Set max  photo size in bytes
                .galleryMimeTypes(gallery) // mime types for gallery
                .filesMimeTypes(files) // mime types for files
                .build(); // Hide any of the three options
    }
  1. Deklarieren Sie registerForActivityResult

Kotlin

 private var mLauncher = registerForActivityResult(StartActivityForResult()) { result ->

    val list =  attachmentManager?.manipulateAttachments(this,result.resultCode,result.data)
    Toast.makeText(this, list?.size.toString(), Toast.LENGTH_LONG).show()
}

Java

 ActivityResultLauncher<Intent> mLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {

        ArrayList<AttachmentDetail> list = attachmentManager.manipulateAttachments(this,result.getResultCode(),result.getData());

        });
  1. Anruf openSelection () Methode zum Anzeigen der Benutzeroberfläche zum Auswählen und Weiterleiten von ActivityResultLauncher

Kotlin

 attachmentManager?.openSelection(mLauncher)

Java

attachmentManager.openSelection(mLauncher);
  1. OnRequestPermissionsResult rückgängig machen (optional)

Kotlin

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        attachmentManager?.handlePermissionResponse(requestCode, permissions, grantResults)
    }

Java

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        attachmentManager.handlePermissionResponse(requestCode,permissions,grantResults);
    }

Andere Verwendungen

  1. Sie können eine Galerie, Kamera oder ein Dateisystem direkt öffnen, ohne dem Benutzer die Benutzerauswahlschnittstelle anzuzeigen

Kotlin

 attachmentManager?.startCamera(mLauncher)
 // OR
 attachmentManager?.openGallery(mLauncher)
 // OR
 attachmentManager?.openFilSystem(mLauncher)

Java

 attachmentManager.startCamera(mLauncher);
 // OR
 attachmentManager.openGallery(mLauncher);
 // OR
 attachmentManager.openFilSystem(mLauncher);

Notiz

Jede Art von Verbesserungen und Vorschlägen sind willkommen. Wenn Sie diese Bibliothek in Ihrem Projekt verwenden, teilen Sie mir bitte auch die URL Ihrer Anwendung mit. Ich werde Ihre Anwendung hier auflisten.

Android Arsenal – Showcase-Ansichten

Android-Bibliothek zum Hervorheben verschiedener Funktionen der Anwendung, die mit Jetpack Compose erstellt wurden.

Die Bibliothek ist inspiriert Tippen Sie auf TargetView Es ist nützlich für geerbte Ansichten.

Aufbau

Fügen Sie es Ihrem root build.gradle am Ende des Repositorys hinzu:

repositories {
    maven { url 'https://jitpack.io' }
}

Sucht hinzufügen

  implementation 'com.github.canopas:Intro-showcase-view:1.0.2'

Wie benutzt man?

@Composable
fun ShowcaseSample() {
    val targets = remember {
        mutableStateMapOf<String, ShowcaseProperty>()
    }
    Box {
        FloatingActionButton(
            onClick = {},
            modifier = Modifier.padding(16.dp).align(Alignment.BottomStart).onGloballyPositioned { coordinates ->
                targets["email"] = ShowcaseProperty(
                    1, coordinates, "Check emails", "Click here to check/send emails"
                )
            },
            backgroundColor = ThemeColor,
            contentColor = Color.White,
            elevation = FloatingActionButtonDefaults.elevation(6.dp)
        ) {
            Icon(
                Icons.Filled.Email,
                contentDescription = "Email"
            )
        }

        IntroShowCase(targets) {
            // Showcase finished!!
        }
    }
}
   

Weitere Informationen finden Sie unter Lernprogramm

Für Fehler, Fragen und Diskussionen verwenden Github-Probleme.

Canopas Software LLP

Copyright 2022 Canopas Software LLP

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Android Arsenal – Dateisystem

Inhalt

Überblick

Je höher das API-Level, desto stärker beschränkt Google den Zugriff auf Dateien im Android-Speicher. Obwohl das Storage Access Framework (SAF) darauf ausgelegt ist, den Benutzerspeicher vor bösartigen Anwendungen zu schützen, macht es uns den Zugriff auf Dateien noch schwerer. Nehmen Sie das Beispiel wo java.io.File ist in Android 10 veraltet.

Die einfache Speicherung erleichtert Ihnen den Zugriff auf und die Verwaltung von Dateien auf allen Ebenen der API. Wenn Sie mehr über den Hintergrund dieser Bibliothek erfahren möchten, lesen Sie diesen Artikel: Framework für einfachen Speicherzugriff in Android mit SimpleStorage

Das Hinzufügen von Simple Storage zu Ihrem Projekt ist ziemlich einfach:

implementation "com.anggrayudi:storage:X.Y.Z"

Wo X.Y.Z ist eine Version der Bibliothek:

Alle Versionen sind zu finden Hier. Benutzen SNAPSHOT Version müssen Sie diese URL zu Root-Gradle hinzufügen:

allprojects {
    repositories {
        google()
        mavenCentral()
        // add this line
        maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
    }
}

Java-Kompatibilität

Simple Storage wurde in Kotlin erstellt. Folge dies Dokumentation um es in Ihrem Java-Projekt zu verwenden.

Terminologie

Andere Terminologie

Dateien lesen

In einfacher Lagerung, DocumentFile Wird verwendet, um auf Dateien zuzugreifen, wenn Ihrer Anwendung vollständiger Speicherzugriff gewährt wurde, einschließlich Lese- und Schreibberechtigungs-URIs. Während MediaFile wird verwendet, um auf Mediendateien zuzugreifen MediaStore keine Speicherberechtigungs-URIs.

Sie können die Hilfsfunktionsdatei einlesen DocumentFileCompat ich MediaStoreCompat:

DocumentFileCompat

  • DocumentFileCompat.fromFullPath()
  • DocumentFileCompat.fromSimplePath()
  • DocumentFileCompat.fromFile()
  • DocumentFileCompat.fromPublicFolder()

Beispiel

val fileFromExternalStorage = DocumentFileCompat.fromSimplePath(context, basePath = "Downloads/MyMovie.mp4")

val fileFromSdCard = DocumentFileCompat.fromSimplePath(context, storageId = "9016-4EF8", basePath = "Downloads/MyMovie.mp4")

MediaStoreCompat

  • MediaStoreCompat.fromMediaId()
  • MediaStoreCompat.fromFileName()
  • MediaStoreCompat.fromRelativePath()
  • MediaStoreCompat.fromFileNameContains()
  • MediaStoreCompat.fromMimeType()
  • MediaStoreCompat.fromMediaType()

Beispiel

val myVideo = MediaStoreCompat.fromFileName(context, MediaType.DOWNLOADS, "MyMovie.mp4")

val imageList = MediaStoreCompat.fromMediaType(context, MediaType.IMAGE)

Dokumentenverwaltung

DocumentFile

Denn java.io.File ist in Android 10 veraltet, also müssen Sie es verwenden DocumentFile um Dateien zu verwalten.

Simple Storage fügt Kotlina-Erweiterungsfunktionen hinzu DocumentFileSo können Sie Dateien wie folgt verwalten:

  • DocumentFile.getStorageId()
  • DocumentFile.getStorageType()
  • DocumentFile.getBasePath()
  • DocumentFile.copyFileTo()
  • List<DocumentFile>.moveTo()
  • DocumentFile.search()
  • DocumentFile.deleteRecursively()
  • DocumentFile.getProperties()
  • DocumentFile.openOutputStream()und vieles mehr…

MediaFile

Sie können ähnliche Optionen für Mediendateien haben DocumentFileoder:

  • MediaFile.absolutePath
  • MediaFile.isPending
  • MediaFile.delete()
  • MediaFile.renameTo()
  • MediaFile.copyFileTo()
  • MediaFile.moveFileTo()
  • MediaFile.openInputStream()
  • MediaFile.openOutputStream()etc.

Fordern Sie Zugang zum Lager an

Obwohl der Benutzer während der Laufzeit Lese- und Schreibberechtigungen erteilt hat, hat Ihre Anwendung möglicherweise noch keinen vollständigen Zugriff auf das Repository, sodass Sie keine Dateien suchen, verschieben und kopieren können. Ob Sie Zugriff auf das Lager haben, können Sie über prüfen SimpleStorage.hasStorageAccess().

Um vollen Zugriff auf das Repository zu gewähren, müssen Sie das SAF öffnen und dem Benutzer erlauben, URIs Lese- und Schreibzugriffsberechtigungen zu erteilen. Diese Bibliothek bietet Ihnen eine Hilfsklasse namens SimpleStorage um den Bewerbungsprozess zu erleichtern:

class MainActivity : AppCompatActivity() {

    private val storage = SimpleStorage(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setupSimpleStorage()
        btnRequestStorageAccess.setOnClickListener {
            storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS)
        }
    }

    private fun setupSimpleStorage() {
        storage.storageAccessCallback = object : StorageAccessCallback {
            override fun onRootPathNotSelected(
                requestCode: Int,
                rootPath: String,
                uri: Uri,
                selectedStorageType: StorageType,
                expectedStorageType: StorageType
            ) {
                MaterialDialog(this@MainActivity)
                    .message(text = "Please select $rootPath")
                    .negativeButton(android.R.string.cancel)
                    .positiveButton {
                        val initialRoot = if (expectedStorageType.isExpected(selectedStorageType)) selectedStorageType else expectedStorageType
                        storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS, initialRoot, expectedStorageType)
                    }.show()
            }

            override fun onCanceledByUser(requestCode: Int) {
                Toast.makeText(baseContext, "Canceled by user", Toast.LENGTH_SHORT).show()
            }

            override fun onStoragePermissionDenied(requestCode: Int) {
                /*
                Request runtime permissions for Manifest.permission.WRITE_EXTERNAL_STORAGE
                and Manifest.permission.READ_EXTERNAL_STORAGE
                */
            }

            override fun onRootPathPermissionGranted(requestCode: Int, root: DocumentFile) {
                Toast.makeText(baseContext, "Storage access has been granted for ${root.getStorageId(baseContext)}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        storage.onSaveInstanceState(outState)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        storage.onRestoreInstanceState(savedInstanceState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storage.onActivityResult(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storage.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
}

Ordnerauswahl

private fun requestStoragePermission() {
    /*
    Request runtime permissions for Manifest.permission.WRITE_EXTERNAL_STORAGE
    and Manifest.permission.READ_EXTERNAL_STORAGE
    */
}

private fun setupFolderPickerCallback() {
    storage.folderPickerCallback = object : FolderPickerCallback {
        override fun onStoragePermissionDenied(requestCode: Int) {
            requestStoragePermission()
        }

        override fun onStorageAccessDenied(requestCode: Int, folder: DocumentFile?, storageType: StorageType) {
            if (storageType == StorageType.UNKNOWN) {
                requestStoragePermission()
                return
            }
            MaterialDialog(this@MainActivity)
                .message(
                    text = "You have no write access to this storage, thus selecting this folder is useless." +
                            "nWould you like to grant access to this folder?"
                )
                .negativeButton(android.R.string.cancel)
                .positiveButton {
                    storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS, storageType)
                }.show()
        }

        override fun onFolderSelected(requestCode: Int, folder: DocumentFile) {
            Toast.makeText(baseContext, folder.getAbsolutePath(baseContext), Toast.LENGTH_SHORT).show()
        }

        override fun onCanceledByUser(requestCode: Int) {
            Toast.makeText(baseContext, "Folder picker canceled by user", Toast.LENGTH_SHORT).show()
        }
    }
}

Dateiauswahl

private fun setupFilePickerCallback() {
    storage.filePickerCallback = object : FilePickerCallback {
        override fun onCanceledByUser(requestCode: Int) {
            Toast.makeText(baseContext, "File picker canceled by user", Toast.LENGTH_SHORT).show()
        }

        override fun onStoragePermissionDenied(requestCode: Int, file: DocumentFile?) {
            requestStoragePermission()
        }

        override fun onFileSelected(requestCode: Int, file: DocumentFile) {
            Toast.makeText(baseContext, "File selected: ${file.name}", Toast.LENGTH_SHORT).show()
        }
    }
}

SimpleStorageHelper

Wenn Sie die Implementierung der Ordner- und Dateiauswahl und der vollständigen Festplattenanforderungen kompliziert genug finden, können Sie verwenden SimpleStorageHelper um den Prozess zu vereinfachen. Diese Hilfsklasse enthält Standardstile für die Verwaltung des Warehouse-Zugriffs.

class MainActivity : AppCompatActivity() {

    private val storageHelper = SimpleStorageHelper(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        storageHelper.onFolderSelected = { requestCode, folder ->
            // do stuff
        }
        storageHelper.onFileSelected = { requestCode, files ->
            // do stuff
        }

        btnRequestStorageAccess.setOnClickListener { storageHelper.requestStorageAccess() }
        btnOpenFolderPicker.setOnClickListener { storageHelper.openFolderPicker() }
        btnOpenFilePicker.setOnClickListener { storageHelper.openFilePicker() }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        storageHelper.onSaveInstanceState(outState)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        storageHelper.onRestoreInstanceState(savedInstanceState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storageHelper.storage.onActivityResult(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storageHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
}

Einfacher, oder?

Verschieben und kopieren: Dateien und Ordner

Simple Storage hilft Ihnen beim Kopieren/Verschieben von Dateien und Ordnern über:

  • DocumentFile.copyFileTo()
  • DocumentFile.moveFileTo()
  • DocumentFile.copyFolderTo()
  • DocumentFile.moveFolderTo()

Sie können beispielsweise einen Ordner mit mehreren Codezeilen verschieben:

val folder: DocumentFile = ...
val targetFolder: DocumentFile = ...

// Since moveFolderTo() is annotated with @WorkerThread, you must execute it in background thread
folder.moveFolderTo(applicationContext, targetFolder, skipEmptyFiles = false, callback = object : FolderCallback() {
    override fun onPrepare() {
        // Show notification or progress bar dialog with indeterminate state
    }

    override fun onCountingFiles() {
        // Inform user that the app is counting & calculating files
    }

    override fun onStart(folder: DocumentFile, totalFilesToCopy: Int, workerThread: Thread): Long {
        return 1000 // update progress every 1 second
    }

    override fun onParentConflict(destinationFolder: DocumentFile, action: FolderCallback.ParentFolderConflictAction, canMerge: Boolean) {
        handleParentFolderConflict(destinationFolder, action, canMerge)
    }

    override fun onContentConflict(
        destinationFolder: DocumentFile,
        conflictedFiles: MutableList<FolderCallback.FileConflict>,
        action: FolderCallback.FolderContentConflictAction
    ) {
        handleFolderContentConflict(action, conflictedFiles)
    }

    override fun onReport(report: Report) {
        Timber.d("onReport() -> ${report.progress.toInt()}% | Copied ${report.fileCount} files")
    }

    override fun onCompleted(result: Result) {
        Toast.makeText(baseContext, "Copied ${result.totalCopiedFiles} of ${result.totalFilesToCopy} files", Toast.LENGTH_SHORT).show()
    }

    override fun onFailed(errorCode: ErrorCode) {
        Toast.makeText(baseContext, "An error has occurred: $errorCode", Toast.LENGTH_SHORT).show()
    }
})

Das Beste an dieser Bibliothek ist, dass Sie Benutzer bitten können, „Zusammenführen“, „Ersetzen“, „Neu erstellen“ oder „Doppelte Ordner und Dateien überspringen“ zu wählen, wenn ein Konflikt über gefunden wird onConflict(). Hier sind Screenshots des Codebeispiels zum Umgang mit Konflikten:

Lesen MainActivity aus dem Beispielcode, wenn Sie die obigen Dialoge imitieren möchten.

FAQ

Probleme? Weiter lesen Häufig gestellte Fragen.

Weitere Beispiele für die Verwendung von SimpleStorage

In diesen Open-Source-Projekten kommt SimpleStorage zum Einsatz. Sehen Sie sich an, wie diese Repositories es verwenden:

Lizenz

Copyright © 2020-2022 Anggrayudi Hardiannico A.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Android Arsenal – Animationen

Eine kleine Android-Bibliothek macht es einfach, die TextView-Zählung zu animieren.

Fügen Sie das CounterAnimationTextView-Widget in Ihr Layout ein.

      <com.github.hamzaahmedkhan.counteranimationtextview.CountAnimationTextView
          android:id="@+id/moneyPositive"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="0"
          android:textColor="@color/black"
          android:textSize="20sp" />

Zählen mit Dauer animieren

    counterAnimationTextView
        .setAnimationDuration(5000)
        .countAnimation(0, 99999)

Animieren Sie Count mit DecimalFormat

    counterAnimationTextView
        .setDecimalFormat(new DecimalFormat("###,###,###"))
        .setAnimationDuration(10000)
        .countAnimation(0, 9999999)

Zählen animieren mit interPolator

    counterAnimationTextView
        .setInterpolator(AccelerateInterpolator())
        .countAnimation(0, 9999999)

Fügen Sie Ihrem build.gradle Abhängigkeiten hinzu.

dependencies {
 implementation 'com.github.hamzaahmedkhan:CounterAnimationTextView:1.0.1'
}

Wenn Ihnen dieses Projekt in irgendeiner Weise hilft, zeigen Sie Ihre Liebe ❤️ durch Setzen von a an diesem Projekt ✌️ …….. AUCH KOSTENLOS ZUM ZEICHNEN UND MITBRINGEN !!