安卓大作业开发01---体温记录
今日简单实现了体温的记录,以及接入了丁香医生的疫情信息。
接下来三天准备学习一下安卓爬取指定网页数据。
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"); } }