Bagaimana saya menguraikan jadwal

Hai Habr!

Pembaca yang budiman! Jika Anda tertarik pada parsing html dan pengembangan Android, maka artikel ini cocok untuk Anda. Saya harap Anda menemukan banyak hal menarik dan berguna di dalamnya. Di dalamnya saya ingin membagikan pengalaman saya di bidang ini.

Deskripsi masalah


Sedikit tentang saya. Saya mahasiswa tahun ketiga ITA SFU. Seperti semua siswa, saya perlu melihat jadwal kelas setiap hari. Dan saya perlu tahu jadwal tidak hanya pada hari berikutnya, tetapi juga satu atau dua minggu sebelumnya.

Tampaknya, mengapa tidak hanya menyimpan jadwal dan menggunakannya? Sayangnya, ada sejumlah alasan yang mencegah hal ini, yaitu:

  • Jadwal untuk satu minggu bisa sangat berbeda dari jadwal untuk yang lain
  • Jadwal tidak konstan dan dapat berubah

Tentu saja, ada situs dengan jadwal, tetapi itu tidak terlalu nyaman, karena hanya menampilkan tabel mentah dengan jadwal selama 20 minggu. Siswa harus membalik halaman besar, mencari jadwal untuk hari yang diinginkan. Selain itu, dalam mode offline, jadwal menjadi tidak tersedia.
Saya memutuskan untuk membuat aplikasi kecil yang dapat menguraikan situs dengan jadwal lembaga saya, dan akan memiliki serangkaian barang berikut:

  • Tampilan: nomor minggu saat ini, tanggal, hari dalam seminggu dan jadwal untuk hari itu
  • Kemampuan untuk menelusuri jadwal dengan tombol "kembali" dan "berikutnya"
  • Jika tidak ada Internet, tunjukkan jadwal offline versi unduhan terbaru

Lanjutkan ke eksekusi


Jadi, sambil menyingsingkan lengan baju saya, saya mulai bekerja. Anda harus mulai dari yang kecil. Yaitu, dari mengedit file manifes. Perlu diingat bahwa aplikasi kita akan bekerja dengan Internet dan sangat penting bagi kita untuk mendapatkan izin yang sesuai:

File manifes
Buka manifests-> AndroidManifest.xml. Tambahkan izin. Hasilnya kira-kira seperti ini:

<?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> 

Sekarang mari kita beralih ke antarmuka. Untuk saat ini, mari fokus pada fungsionalitas dan bukan penyalahgunaan widget. Karena itu, saya hanya menempatkan empat widget: Judul, kotak teks dan tombol: bolak-balik.

Markup Aktivitas
 <?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> 

Sekarang mari kita mulai parsing. Di sinilah Jsoup parser open source yang luar biasa membantu kami. Saya segera menolak opsi menggunakan WebView, karena saya menemukan metode ini sangat tidak nyaman. Selain itu, saya tidak benar-benar ingin menggunakan widget tambahan, yang tanpanya Anda dapat dengan mudah melakukannya.

Koneksi Jsoup
Tambahkan dependensi ke build.gradle:
 implementation 'org.jsoup:jsoup:1.11.1' 


Jangan lupa bahwa bekerja dengan web untuk Android adalah tugas yang sulit. Agar aplikasi tidak macet, Anda harus bekerja dengan web yang terletak di luar aliran UI. Oleh karena itu, kita akan menggunakan kelas AsyncTask. Kami akan memasukkan fungsionalitas dasar ke dalamnya, dan kemudian hanya mentransfer data ke aliran UI.

Bagi mereka yang tidak terbiasa dengan AsyncTask, saya ingin mengatakan bahwa kelas ini harus berada di dalam kelas aktivitas Anda. Kelas itu sendiri ditunjukkan di bawah ini.

Kode aktivitas dengan kelas AsyncTask
 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; // public TextView weeknumber; public EditText timetable; public Button next; public Button down; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); offline = false;//     count = 0;//      weeknumber = findViewById(R.id.WeekNumber); timetable = findViewById(R.id.timetable); next = findViewById(R.id.next); down = findViewById(R.id.down); getting AsyncTask = new getting(); AsyncTask.execute(); } class getting extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { super.onPreExecute(); //         } @Override protected String doInBackground(String... params) { /*             html  */ String answer = "";//         .     String url = "https://ictis.sfedu.ru/rasp/HTML/82.htm";//     Document document = null; try { document = Jsoup.connect(url).get();//     answer = document.body().html();//     body  } catch (IOException e) { //   ,   ,     //    answer    txt  try { BufferedReader read = new BufferedReader(new InputStreamReader(openFileInput("timetable.txt"))); String str = ""; while ((str = read.readLine())!=null){ answer +=str; } read.close(); offline = true;//    } catch (FileNotFoundException ex) { //     ,    answer  answer = ""; ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } //    html // html      nolessone //   answer = answer.replace("","") .replace("","") .replace("<br>","br") .replace("<font face=\"Arial\" size=\"1\"></font><p align=\"CENTER\"><font face=\"Arial\" size=\"1\"></font>","nolessone")// ""    nolessone .replace(" ",""); return Jsoup.parse(answer).text();//      answer    UI- } @Override protected void onPostExecute(String result) { super.onPostExecute(result); /*            */ request = "";//   String temp = result.toString();//   //  ,   timetable.txt,        try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(openFileOutput("timetable.txt",MODE_PRIVATE))); writer.write(temp); writer.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } boolean start = false; for(String str:temp.split(": ")){ if(start) { //      newweek    request request += "newweek"+str.split("")[0] + "\n"; } start = true; } //      newday,     request = request.replace("","newday").replace("","newday") .replace("","newday").replace("","newday") .replace("","newday").replace("","newday"); /*    count = 0,       count = -1,    count = 1,    . */ Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR,count); Date dayformat = calendar.getTime(); SimpleDateFormat format = new SimpleDateFormat("dd MMMM"); //  timetable.setText(request); if(offline && !temp.equals("")){ // ,      Toast.makeText(getApplicationContext(),"   !",Toast.LENGTH_LONG).show(); } //    ,    if(temp.equals("")){ Toast.makeText(getApplicationContext()," !",Toast.LENGTH_LONG).show(); } } } } 


Akibatnya, kami mendapatkan data dalam formulir ini:

Jadwal satu minggu


Mari kita menganalisis metode yang kita gunakan:

Buat elemen bertipe Dokumen

 Document document = null; 

Kami mendapatkan halamannya

 document = Jsoup.connect(url).get(); 

Sekarang kita mendapatkan isi dari tag tubuh

 answer = document.body().html(); 

Jsoup juga dapat mengambil konten dari tag inti lainnya. Misalnya, Anda bisa mendapatkan judul halaman menggunakan metode title (), dll. Metode Html () Mengembalikan kode html, dan teks () adalah teks biasa tanpa tag html.

Setelah menerima kode html, Anda dapat mengubahnya menjadi teks biasa, menghapus semua tag. Ini dapat dilakukan dengan menggunakan parse (htmlcode) .text ():

 return Jsoup.parse(answer).text(); 

Saya ingin berbagi beberapa metode Jsoup yang lebih bermanfaat yang tidak digunakan:

 Element link = document.select("tag");//    String url = link.attr("attribute"); //    

Gambar di spoiler di atas adalah contoh dari jadwal satu minggu. Bahkan, 20 minggu seperti itu akan dikembalikan kepada kami. Sekarang tugas kita adalah menemukan hari ini di kumpulan data ini dan menampilkannya.

Bawa ke pikiran


Jadi apa yang kita miliki? Kami mempelajari cara memasukkan kode html halaman ke string yang dapat dengan mudah diurai. Ini dapat dengan mudah dilakukan dengan menggunakan metode string .split () dan .replace ().

Secara umum, algoritma akan terlihat seperti ini.

Pertama kita mendapatkan tanggal yang diinginkan dari Android. Lalu kami melakukan dua siklus, satu bersarang di yang lain. Siklus pertama berjalan melalui minggu, yang kedua, yang ada di dalamnya, berjalan melalui hari-hari dalam seminggu. Jika tanggal hari itu bertepatan dengan tanggal yang diterima dari Android, maka kami menampilkan jadwal hari ini di kotak teks. Namun, semua orang dapat menulis algoritma ini dengan caranya sendiri. Saya melampirkan versi implementasinya.

Kode Kegiatan Lengkap
 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; // public TextView weeknumber; public EditText timetable; public Button next; public Button down; public void formating(String day){ String DayTimetable = ""; String[] weeks = request.split("newweek"); String DayData = "";//      /*                 less      tich       aud  ,      ( -230) */ String less[] = new String[7]; String tich[] = new String[7]; String aud[] = new String[7]; for(String thisweek:weeks){//   if(thisweek.indexOf(day) != -1) {//        ... WeekNumber = thisweek.split(" ")[0];//   for(String thisday:thisweek.split("newday")){//      if(thisday.indexOf(day) != -1) {//       , ... // ,       newless //      . . .  . thisday = thisday.replace("no","newless") .replace(".","newless.") .replace(".","newless.") .replace(".","newless."); int i = 0; for(String thislessone:thisday.split("newless")) {//      if(i != 0) { String[] ScienceInformation = thislessone.replace("br ","").split("br"); String science = ScienceInformation[0]; science = science.replace("lessone",""); String ticher = ""; if(ScienceInformation.length > 1) ticher = ScienceInformation[1]; DayTimetable += i + "-:  - " + science+"\n"+ticher+"\n\n"; ticher = ticher.replace("-","@-").replace("-","@-") .replace("-","@-").replace("-","@-") .replace("-","@-").replace("-","@-") .replace("-","@-").replace("K-","@K-"); String Auditory; if(ticher.split("@").length == 2){ Auditory = ": "+ticher.split("@")[1]; }else Auditory = ": ";//     ticher = ticher.split("@")[0]; if(ticher.length() >0){ ticher = ": "+ticher; }else{ ticher = ""; } if(i==1){ less[i-1] = "1- (8:00-9:35) "+science; } if(i==2){ less[i-1] = "2- (9:50-11:25) "+science; } if(i==3){ less[i-1] = "3- (11:55-13:30) "+science; } if(i==4){ less[i-1] = "4- (13:45-15:20) "+science; } if(i==5){ less[i-1] = "5- (15:50-17:25) "+science; } if(i==6){ less[i-1] = "6- (17:40-19:15) "+science; } if(i==7){ less[i-1] = "7- (19:30-21:05) "+science; } tich[i-1] = ticher; aud[i-1] = Auditory; }else DayData = thislessone;// i=0  thislessone     i++; } } } } } timetable.setText(DayData);//  for(int i = 0; i <=6; i++){ timetable.setText(timetable.getText()+"\n"+less[i]+tich[i]+aud[i]);// ,      (   ) } weeknumber.setText(" "+ WeekNumber + " ");//   } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); offline = false;//     count = 0; weeknumber = findViewById(R.id.WeekNumber); timetable = findViewById(R.id.timetable); next = findViewById(R.id.next); down = findViewById(R.id.down); getting getting = new getting(); getting.execute(); //      next.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { 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)); } }); down.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { 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)); } }); } class getting extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { super.onPreExecute(); //         getSupportActionBar().setTitle("..."); } @Override protected String doInBackground(String... params) { /*             html  */ String answer = "";//         .     String url = "https://ictis.sfedu.ru/rasp/HTML/82.htm";//     Document document = null; try { document = Jsoup.connect(url).get();//     answer = document.body().html();//     body  } catch (IOException e) { //   ,   ,     //    answer    txt  try { BufferedReader read = new BufferedReader(new InputStreamReader(openFileInput("timetable.txt"))); String str = ""; while ((str = read.readLine())!=null){ answer +=str; } read.close(); offline = true;//    } catch (FileNotFoundException ex) { //     ,    answer  answer = ""; ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } //    html // html      nolessone //   answer = answer.replace("","") .replace("","") .replace("<br>","br") .replace("<font face=\"Arial\" size=\"1\"></font><p align=\"CENTER\"><font face=\"Arial\" size=\"1\"></font>","nolessone") .replace(" ",""); return Jsoup.parse(answer).text();//      answer    UI- } @Override protected void onPostExecute(String result) { super.onPostExecute(result); /*            */ request = "";//   String temp = result.toString();//   //  ,   timetable.txt,        try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(openFileOutput("timetable.txt",MODE_PRIVATE))); writer.write(temp); writer.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } boolean start = false; for(String str:temp.split(": ")){ if(start) { //      newweek    request request += "newweek"+str.split("")[0] + "\n"; } start = true; } //      newday,     request = request.replace("","newday").replace("","newday") .replace("","newday").replace("","newday") .replace("","newday").replace("","newday"); /*    count = 0,       count = -1,    count = 1,    . */ 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)); if(offline && !temp.equals("")){ // ,      Toast.makeText(getApplicationContext(),"   !",Toast.LENGTH_LONG).show(); } //    ,    if(temp.equals("")){ Toast.makeText(getApplicationContext()," !",Toast.LENGTH_LONG).show(); } getSupportActionBar().setTitle(""); } } } 


mengambil jadwal terjadi dalam metode formating (). Dengan mengirimkan tanggal ke metode input, kami akan mendapatkan jadwal untuk hari itu. Jadi kita dapat dengan mudah mengimplementasikan kode untuk tombol "kembali" dan "selanjutnya"

Kode tombol selanjutnya:

 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));//  formating 

Menggunakan Kalender, kami mendapatkan tanggal hari ini. Dengan menggunakan metode add, kami menambahkan jumlah hari yang dicatat dalam hitungan ke tanggal hari ini. Kode untuk tombol kembali akan serupa, hanya hitungan yang perlu dikurangi nilainya.

Tangkapan layar


Kesimpulan


Tentu saja, Anda bisa mengerjakan desainnya, tapi itu topik lain. Saya hanya ingin berbagi teknologi dasar. Pada spoiler di bawah ini, saya memasang screenshot dengan desain yang ditingkatkan. Saya juga menambahkan beberapa fungsi, misalnya: pengaturan, kemampuan untuk memilih grup studi, dll. Aplikasi itu sendiri dapat dilihat sedikit kemudian, segera setelah saya mengingatnya.

Tangkapan layar aplikasi

Source: https://habr.com/ru/post/id483284/


All Articles