今日簡單實現了體溫的記錄,以及接入了丁香醫生的疫情信息。
接下來三天准備學習一下安卓爬取指定網頁數據。
1、數據庫
1、entity
只需要簡單的記錄體溫和時間即可,所以entity的設計並不復雜。
package com.example.fightvirus; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.PrimaryKey; @Entity public class Record { @PrimaryKey(autoGenerate = true) private int id; @ColumnInfo(name = "heat") private float heat; @ColumnInfo(name = "time") private String time; public int getId() { return id; } public void setId(int id) { this.id = id; } public float getHeat() { return heat; } public void setHeat(float heat) { this.heat = heat; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
2、dao
只需要實現添加刪除和查詢所有功能即可。
package com.example.fightvirus; import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; import androidx.room.Query; import java.util.List; @Dao public interface RecordDao { @Delete void deleteRecord(Record... records); @Query("select * from record") LiveData<List<Record>> getAllRecords(); @Insert void insertRecord(Record... records); }
3、database
package com.example.fightvirus; import android.content.Context; import androidx.room.Database; import androidx.room.Room; import androidx.room.RoomDatabase; @Database(entities = {Record.class},version = 1,exportSchema = false) public abstract class RecordDatabase extends RoomDatabase { private static RecordDatabase instance; static synchronized RecordDatabase getDatabase(Context context){ if (instance == null){ instance = Room.databaseBuilder(context.getApplicationContext(),RecordDatabase.class,"heat_database") .build(); } return instance; } public abstract RecordDao getRecordDao(); }
4、repository
這里還是采用了通過工廠來進行dao的操作。
package com.example.fightvirus; import android.content.Context; import android.os.AsyncTask; import androidx.lifecycle.LiveData; import java.util.List; public class RecordRepository { private RecordDao recordDao; private LiveData<List<Record>> listLiveData; public RecordRepository(Context context) { RecordDatabase database = RecordDatabase.getDatabase(context); recordDao = database.getRecordDao(); listLiveData = recordDao.getAllRecords(); } public void insertRecord(Record... records){ new InsertAsycTask(recordDao).execute(records); } public void deleteRecord(Record... records){ new DeleteAsycTask(recordDao).execute(records); } public LiveData<List<Record>> getAllRecords(){ return listLiveData; } static class InsertAsycTask extends AsyncTask<Record,Void,Void>{ RecordDao recordDao; public InsertAsycTask(RecordDao recordDao) { this.recordDao = recordDao; } @Override protected Void doInBackground(Record... records) { recordDao.insertRecord(records); return null; } } static class DeleteAsycTask extends AsyncTask<Record,Void,Void>{ RecordDao recordDao; public DeleteAsycTask(RecordDao recordDao) { this.recordDao = recordDao; } @Override protected Void doInBackground(Record... records) { recordDao.deleteRecord(records); return null; } } }
5、viewModel
通過viewModel來進行對repository操作。
package com.example.fightvirus; import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import java.util.List; public class RecordViewModel extends AndroidViewModel { private RecordRepository repository; public RecordViewModel(@NonNull Application application) { super(application); repository = new RecordRepository(application); } public void insertRecord(Record... records){ repository.insertRecord(records); } public void deleteRecord(Record... records){ repository.deleteRecord(records); } public LiveData<List<Record>> getAllRecords(){ return repository.getAllRecords(); } }
2、界面
本次采用了底部導航欄來進行體溫記錄以及疫情信息查看,所以需要自己設計一個menu。
1、menu

2、體溫列表頁面

使用floatingActionButton來跳轉到體溫記錄頁面。
3、丁香醫生接入頁面
因為使用了AgentWeb,所以該頁面不用進行頁面的布局等處理,只需將頂層容器換為LinearLayout即可。
4、體溫記錄頁面

5、頁面之間的關系(navigation)

-
體溫列表頁面和體溫添加頁面有先后關系,建立關聯。
-
體溫列表頁面和丁香醫生顯示頁面並無先后關系,不用處理。
6、主頁面

3、主要邏輯代碼
1、主界面
package com.example.fightvirus; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import android.content.Context; import android.os.Bundle; import android.view.inputmethod.InputMethodManager; import com.google.android.material.bottomnavigation.BottomNavigationView; import java.sql.RowId; public class MainActivity extends AppCompatActivity { NavController navController; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); navController = Navigation.findNavController(findViewById(R.id.fragment)); BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView); AppBarConfiguration configuration = new AppBarConfiguration.Builder(bottomNavigationView.getMenu()).build(); NavigationUI.setupActionBarWithNavController(this,navController,configuration); NavigationUI.setupWithNavController(bottomNavigationView,navController); } @Override public boolean onSupportNavigateUp() { InputMethodManager inputMethodManager = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(findViewById(R.id.fragment).getWindowToken(),0); navController.navigateUp(); return super.onSupportNavigateUp(); } }
主界面主要加上底部導航欄的實現代碼以及進入體溫記錄頁面后的返回操作。
2、體溫列表
package com.example.fightvirus; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import java.util.List; /** * A simple {@link Fragment} subclass. */ public class ListFragment extends Fragment { private RecordViewModel viewModel; private MyAdapter myAdapter; private RecyclerView recyclerView; private FloatingActionButton floatingActionButton; private LiveData<List<Record>> listLiveData; private List<Record> allRecords; public ListFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_list, container, false); viewModel = ViewModelProviders.of(requireActivity()).get(RecordViewModel.class); recyclerView = view.findViewById(R.id.recyclerView); floatingActionButton = view.findViewById(R.id.floatingActionButton); myAdapter = new MyAdapter(); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity())); recyclerView.setAdapter(myAdapter); listLiveData = viewModel.getAllRecords(); //列表前方id刷新 recyclerView.setItemAnimator(new DefaultItemAnimator(){ @Override public void onAnimationFinished(@NonNull RecyclerView.ViewHolder viewHolder) { super.onAnimationFinished(viewHolder); LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); if (linearLayoutManager != null){ int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); int lastPosition = linearLayoutManager.findLastVisibleItemPosition(); for (int i = firstPosition;i<=lastPosition;i++){ MyAdapter.MyViewHolder myViewHolder = (MyAdapter.MyViewHolder) recyclerView.findViewHolderForAdapterPosition(i); if (myViewHolder != null){ myViewHolder.textViewNumber.setText(String.valueOf(i +1)); } } } } }); //左右滑動刪除 new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.START) { @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { final Record record = allRecords.get(viewHolder.getAdapterPosition()); viewModel.deleteRecord(record); Snackbar.make(requireView().findViewById(R.id.showFragment),"刪除此條記錄",Snackbar.LENGTH_LONG) .setAction("取消", new View.OnClickListener() { @Override public void onClick(View v) { viewModel.insertRecord(record); } }).show(); } }).attachToRecyclerView(recyclerView); //數據監聽,列表刷新 listLiveData.observe(getViewLifecycleOwner(), new Observer<List<Record>>() { @Override public void onChanged(List<Record> records) { allRecords = records; int temp = myAdapter.getItemCount(); if (temp != records.size()){ myAdapter.submitList(records); } } }); floatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { NavController controller = Navigation.findNavController(v); controller.navigate(R.id.action_listFragment_to_addFragment); } }); } }
主要的操作和前天的單詞記錄程序相似。
3、體溫記錄頁面
package com.example.fightvirus; import android.content.Context; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; import androidx.navigation.NavController; import androidx.navigation.Navigation; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.text.SimpleDateFormat; import java.util.Date; /** * A simple {@link Fragment} subclass. */ public class AddFragment extends Fragment { private EditText editTextHeat; private Button buttonAdd; private RecordViewModel viewModel; public AddFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_add, container, false); editTextHeat = view.findViewById(R.id.editTextHeat); buttonAdd = view.findViewById(R.id.buttonAdd); viewModel = ViewModelProviders.of(requireActivity()).get(RecordViewModel.class); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); buttonAdd.setEnabled(false); TextWatcher textWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { String heat = editTextHeat.getText().toString().trim(); if (!heat.isEmpty()){ buttonAdd.setEnabled(true); }else { buttonAdd.setEnabled(false); } } @Override public void afterTextChanged(Editable s) { } }; editTextHeat.addTextChangedListener(textWatcher); buttonAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Record record = new Record(); record.setHeat(Float.valueOf(editTextHeat.getText().toString().trim())); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd"); record.setTime(simpleDateFormat.format(new Date())); viewModel.insertRecord(record); InputMethodManager inputMethodManager = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(requireActivity().findViewById(R.id.fragment).getWindowToken(),0); NavController navController = Navigation.findNavController(v); navController.navigateUp(); } }); } }
這個界面比較簡單,加上了輸入框的監聽,如果沒有輸入內容便不能點擊添加按鈕。
4、丁香醫生接入頁面
package com.example.fightvirus; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; import com.just.agentweb.AgentWeb; /** * A simple {@link Fragment} subclass. */ public class StateFragment extends Fragment { private WebView webView; public StateFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_state, container, false); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // webView.getSettings().setJavaScriptEnabled(true); // webView.setWebViewClient(new WebViewClient()); // webView.loadUrl("https://ncov.dxy.cn/ncovh5/view/pneumonia") AgentWeb mAgentWeb = AgentWeb.with(this) .setAgentWebParent((LinearLayout) getView(), new LinearLayout.LayoutParams(-1, -1)) .useDefaultIndicator() .createAgentWeb() .ready() .go("https://ncov.dxy.cn/ncovh5/view/pneumonia"); } }
