Hallo Habr!
Lieber Leser! Wenn Sie sich für HTML-Analyse und Android-Entwicklung interessieren, ist dieser Artikel genau das Richtige für Sie. Ich hoffe, Sie finden darin viele interessante und nützliche Dinge. Darin möchte ich meine Erfahrungen auf diesem Gebiet teilen.
Problembeschreibung
Ein bisschen über mich. Ich bin ein Student im dritten Jahr der ITA SFU. Wie alle Schüler muss ich mir jeden Tag den Stundenplan ansehen. Und ich muss den Zeitplan nicht nur am nächsten Tag kennen, sondern auch ein oder zwei Wochen im Voraus.
Es scheint, warum nicht einfach den Zeitplan speichern und ihn verwenden? Leider gibt es eine Reihe von Gründen, die dies verhindern:
- Der Zeitplan für eine Woche kann sich stark vom Zeitplan für eine andere Woche unterscheiden
- Der Zeitplan ist nicht konstant und kann sich ändern
Natürlich gibt es eine Site mit einem Zeitplan, aber das ist nicht sehr praktisch, da nur eine unformatierte Tabelle mit einem Zeitplan für 20 Wochen angezeigt wird. Der Schüler muss eine große Seite umblättern und nach einem Zeitplan für den gewünschten Tag suchen. Darüber hinaus ist der Zeitplan im Offlinemodus nicht mehr verfügbar.
Ich beschloss, eine kleine Anwendung zu erstellen, die die Site mit dem Zeitplan meines Instituts parsen und die folgenden Goodies enthalten würde:
- Anzeige: aktuelle Wochennummer, Datum, Wochentag und Zeitplan für diesen Tag
- Die Möglichkeit, mit den Schaltflächen "Zurück" und "Weiter" durch den Zeitplan zu scrollen
- Wenn kein Internet verfügbar ist, zeigen Sie die zuletzt heruntergeladene Offline-Version des Zeitplans an
Fahren Sie mit der Ausführung fort
Also krempelte ich die Ärmel hoch und machte mich an die Arbeit. Sie müssen klein anfangen. Nämlich von der Bearbeitung der Manifestdatei. Es ist zu beachten, dass unsere Anwendung mit dem Internet funktioniert und es für uns sehr wichtig ist, die entsprechende Erlaubnis einzuholen:
Manifest-DateiGehen Sie zu manifestests-> AndroidManifest.xml. Berechtigung hinzufügen. Das Ergebnis ist ungefähr so:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
Kommen wir nun zur Benutzeroberfläche. Konzentrieren wir uns zunächst auf die Funktionalität und nicht auf den Missbrauch von Widgets. Daher habe ich nur vier Widgets platziert: Titel, Textfeld und Schaltflächen: vor und zurück.
Aktivitäts-Markup <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/WeekNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" " app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/timetable" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="100dp" android:ems="10" android:inputType="textMultiLine" android:text="" app:layout_constraintTop_toBottomOf="@+id/WeekNumber" tools:layout_editor_absoluteX="0dp" /> <Button android:id="@+id/next" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" app:layout_constraintBottom_toBottomOf="parent" tools:layout_editor_absoluteX="0dp"></Button> <Button android:id="@+id/down" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" app:layout_constraintBottom_toTopOf="@+id/next" tools:layout_editor_absoluteX="0dp"></Button> </androidx.constraintlayout.widget.ConstraintLayout>
Beginnen wir jetzt mit dem Parsen. Hier hilft uns der wunderbare Open Source Parser Jsoup. Ich habe die Option mit WebView sofort verworfen, da ich diese Methode als äußerst unpraktisch empfand. Außerdem wollte ich eigentlich kein zusätzliches Widget verwenden, auf das man problemlos verzichten kann.
Jsoup-VerbindungFügen Sie die Abhängigkeit zu build.gradle hinzu:
implementation 'org.jsoup:jsoup:1.11.1'
Vergessen Sie nicht, dass die Arbeit mit Web für Android eine schwierige Aufgabe ist. Um zu verhindern, dass die Anwendung hängen bleibt, müssen Sie mit dem Web außerhalb des UI-Streams arbeiten. Daher verwenden wir die AsyncTask-Klasse. Wir werden die grundlegenden Funktionen einbinden und die Daten dann einfach in den UI-Stream übertragen.
Für diejenigen, die nicht mit AsyncTask vertraut sind, möchte ich sagen, dass sich diese Klasse in der Klasse Ihrer Aktivität befinden sollte. Die Klasse selbst ist unten gezeigt.
Aktivitätscode mit AsyncTask-Klasse package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class MainActivity extends AppCompatActivity { public boolean offline; public String request; public String WeekNumber; public int count;
Als Ergebnis erhalten wir die Daten in dieser Form:
Lassen Sie uns die von uns verwendeten Methoden analysieren:
Erstellen Sie ein Element vom Typ Dokument
Document document = null;
Wir bekommen die Seite
document = Jsoup.connect(url).get();
Jetzt erhalten wir den Inhalt des Body-Tags
answer = document.body().html();
Jsoup kann auch den Inhalt anderer Core-Tags abrufen. Beispielsweise können Sie den Seitentitel mit der title () -Methode usw. abrufen. Html () -Methode Gibt HTML-Code zurück, und text () ist einfacher Text ohne HTML-Tags.
Nachdem Sie den HTML-Code erhalten haben, können Sie ihn in einfachen Text konvertieren und alle Tags entfernen. Dies kann mit parse (htmlcode) .text () geschehen:
return Jsoup.parse(answer).text();
Ich möchte einige weitere nützliche Jsoup-Methoden vorstellen, die nicht verwendet wurden:
Element link = document.select("tag");
Das Bild oben im Spoiler ist ein Beispiel für einen einwöchigen Zeitplan. Tatsächlich werden 20 solcher Wochen an uns zurückgegeben. Jetzt ist es unsere Aufgabe, heute in diesem Datensatz zu suchen und anzuzeigen.
Denken Sie daran
Also, was haben wir? Wir haben gelernt, den HTML-Code einer Seite in eine Zeichenfolge umzuwandeln, die leicht analysiert werden kann. Dies kann einfach mit den String-Methoden .split () und .replace () durchgeführt werden.
Im Allgemeinen sieht der Algorithmus so aus.
Zuerst bekommen wir den gewünschten Termin von Android. Dann machen wir zwei ineinander verschachtelte Zyklen. Der erste Zyklus durchläuft die Wochen, der zweite, der sich darin befindet, durchläuft die Wochentage. Wenn das Datum des Tages mit dem von Android erhaltenen Datum übereinstimmt, wird der Zeitplan für diesen Tag im Textfeld angezeigt. Jeder kann diesen Algorithmus jedoch auf seine eigene Weise schreiben. Ich habe meine Version der Implementierung angehängt.
Vollständiger Aktivitätscode package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class MainActivity extends AppCompatActivity { public boolean offline; public String request; public String WeekNumber; public int count;
Das Abrufen des Zeitplans erfolgt in der formating () -Methode. Durch Übermitteln eines Datums an die Eingabemethode erhalten wir einen Zeitplan für diesen Tag. So können wir einfach den Code für die Buttons "zurück" und "weiter" implementieren
Code der nächsten Schaltfläche:
count++; Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR,count); Date dayformat = calendar.getTime(); SimpleDateFormat format = new SimpleDateFormat("dd MMMM"); formating(format.format(dayformat));
Mit dem Kalender erhalten wir das heutige Datum. Mit der add-Methode addieren wir die Anzahl der Tage zum heutigen Datum. Der Code für die Zurück-Taste ist ähnlich, nur die Zählung muss den Wert verringern.
Fazit
Natürlich können Sie am Design arbeiten, aber das ist ein anderes Thema. Ich wollte nur die grundlegenden Technologien teilen. In den Spoilern unten habe ich Screenshots mit einem verbesserten Design angehängt. Ich habe auch verschiedene Funktionen hinzugefügt, zum Beispiel: Einstellungen, die Möglichkeit, eine Lerngruppe auszuwählen, usw. Die Anwendung selbst kann etwas später eingesehen werden, sobald ich daran denke.