制作單詞記錄App(一)
- 制作單詞記錄App(一)
-
- 修正性能上的小bug
- 開始制作新單詞界面
本文為學習類文檔,通過學習B站up主longway777的視訊,再加上自己的總結與了解的學習類文章,如有侵權,請聯系部落客進行删除
制作單詞記錄App(一)
這次制作的單詞記錄App是根據前面學習的Room元件,migrantion等
修正性能上的小bug
在之前建立的MyAdapter中,針對詞彙的點選跳轉界面和開關隐藏中文意思,我們将點選事件放在了onBindViewHolder中,這個函數會被系統在運作過程中多次調用,會造成性能上的重複開銷。
可以将其放在onCreateViewHolder中(建立一次即可)。
- 在onCreateViewHolder中定義itemView的ViewHolder對象
final MyViewHolder holder = new MyViewHolder(itemView);
- 調用holder的點選事件方法(處理word在建立時無法擷取)
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
...
final MyViewHolder holder = new MyViewHolder(itemView);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("http://dict.youdao.com/w/" + holder.textViewEnglish.getText() + "/#keyfrom=dict2.top");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
holder.itemView.getContext().startActivity(intent);
}
});
holder.aSwitchChineseInvisible.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Word word = (Word) holder.itemView.getTag(R.id.word_for_view_holder);//廣義object要強制轉換
if (isChecked) {
holder.textViewChinese.setVisibility(View.GONE);
word.setChineseInvisible(true);
wordViewModel.updateWords(word);
} else {
holder.textViewChinese.setVisibility(View.VISIBLE);
word.setChineseInvisible(false);
wordViewModel.updateWords(word);
}
}
});
return holder; //改為傳回holder類型
}
- 在onBindViewHolder中定義word的itemView的setTag()方法(友善使用者在create中使用getTag()方法擷取資料。)
//itemView的setTag方法可以擷取對象并在其他地方通過getTag擷取對應對象
holder.itemView.setTag(R.id.word_for_view_holder,word);
- 為了保證setTag在整個項目中保證全局唯一特性,設定int型參數,可以通過建立資源類将id存入以保證不沖突:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="word_for_view_holder" type="id"/>
</resources>
- 運作之後無變化,性能提高
開始制作新單詞界面
- 建立兩個fragement用來顯示添加界面和單詞顯示界面
- 建立資源類navigation檔案并連結兩個fragement(wordfragement、addfragement),建立兩者的動作聯系。(可以自己選擇制作進出動畫)
- 在Activity中删除之前建立的控件并導入NavHostFragement入口(在container控件中)完成系統連結
- 在wordfragement搭建界面,放入一個recyclerView
- 填寫wordfragement關聯代碼
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Adapter;
import java.util.List;
/**
* A simple {@link Fragment} subclass.
*/
public class WordsFragment extends Fragment {
private WordViewModel wordViewModel;
private RecyclerView recyclerView;
private MyAdapter myAdapter1,myAdapter2;
public WordsFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_words, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
wordViewModel = new ViewModelProvider(requireActivity()).get(WordViewModel.class);
recyclerView = requireActivity().findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));
myAdapter1 = new MyAdapter(false, wordViewModel);//第一個布局不适用卡片布局,是以為false
myAdapter2 = new MyAdapter(true,wordViewModel);
recyclerView.setAdapter(myAdapter1);
wordViewModel.getAllWordsLive().observe(requireActivity(), new Observer<List<Word>>() {
@Override
public void onChanged(List<Word> words) {
int temp = myAdapter1.getItemCount();
myAdapter1.setAllWords(words);
myAdapter2.setAllWords(words);
if (temp!=words.size()) {
myAdapter1.notifyDataSetChanged();
myAdapter2.notifyDataSetChanged();
}
}
});
}
}
- 删除之前在MainActivity中的代碼并準備重寫MainActivity代碼(此處不該寫也能運作之前制作的界面,原因是将activity中的大部分代碼功能移植到了fragement中)
-
在wordsFragement中制作添加按鍵(使用自帶的FloatingActionButton)
建立矢量圖示res->new->Vector Asset,選擇add圖示,背景色為白色,并建立。
将FloatingActionButton拖動到界面中,并設定它的Layout_gravity(Bottom——true,CenterHorizontal——true)屬性和Layout_margin屬性
- 補充wordsFragement代碼:
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Adapter;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
/**
* A simple {@link Fragment} subclass.
*/
public class WordsFragment extends Fragment {
...
private FloatingActionButton floatingActionButton; //定義變量
...
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
floatingActionButton = requireActivity().findViewById(R.id.floatingActionButton);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavController navController = Navigation.findNavController(v);
navController.navigate(R.id.action_wordsFragment_to_addFragment);
}
});
}
}
- 在導覽列設定傳回按鍵:
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private NavController navController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(findViewById(R.id.fragment));
NavigationUI.setupActionBarWithNavController(this,navController);
}
@Override
public boolean onSupportNavigateUp() {
navController.navigateUp();
return super.onSupportNavigateUp();
}
}
- 修改Fragement的Lable
- Add頁面的制作:
<?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:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AddFragment" >
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加單詞"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
<EditText
android:id="@+id/editTextEnglish"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="English Word"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2" />
<EditText
android:id="@+id/editTextChinese"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="中文釋義"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<Button
android:id="@+id/buttonSubmit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.4" />
</androidx.constraintlayout.widget.ConstraintLayout>
- Add界面邏輯代碼:
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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;
/**
* A simple {@link Fragment} subclass.
*/
public class AddFragment extends Fragment {
private Button buttonSubmit;
private EditText editTextEnglish,editTextChinese;
private WordViewModel wordViewModel;
public AddFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_add, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
wordViewModel = new ViewModelProvider(requireActivity()).get(WordViewModel.class);
buttonSubmit = requireActivity().findViewById(R.id.buttonSubmit);
editTextEnglish = requireActivity().findViewById(R.id.editTextEnglish);
editTextChinese = requireActivity().findViewById(R.id.editTextChinese);
//有效資訊不足時,按鍵設定為灰色狀态
buttonSubmit.setEnabled(false);
//進入界面即可彈出鍵盤以節省操作,人性化展現
editTextEnglish.requestFocus();//光标擷取焦點
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editTextEnglish,0);//強制顯示鍵盤,兩個參數為(1. 關聯的View;2. flag)
//制作edit監聽器
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 english = editTextEnglish.getText().toString().trim();
String chinese = editTextChinese.getText().toString().trim();
buttonSubmit.setEnabled(!english.isEmpty() && !chinese.isEmpty());
}
@Override
public void afterTextChanged(Editable s) {
}
};
//添加到edit中
editTextEnglish.addTextChangedListener(textWatcher);
editTextChinese.addTextChangedListener(textWatcher);
//設定按鍵監聽器,将資料存儲到資料庫中,并傳回word界面
buttonSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String english = editTextEnglish.getText().toString().trim();
String chinese = editTextChinese.getText().toString().trim();
Word word = new Word(english,chinese);
wordViewModel.insertWords(word); //資料添加
NavController navController = Navigation.findNavController(v);
navController.navigateUp();
//傳回界面時隐藏鍵盤的操作
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(),0);//兩個參數
}
});
}
}
-
處理因鍵盤彈出而自動壓縮界面的操作:
在清單檔案中設定activity的windowSoftInputMode=“adjustNothing”——不做任何調整:
<activity android:name="com.example.words.MainActivity"
android:windowSoftInputMode="adjustNothing">
-
處理因點選傳回自設按鍵鍵盤不收回的設定:
在MainActivity中的onSupportNavigateUp()中補充代碼:
@Override
public boolean onSupportNavigateUp() {
//傳回界面時隐藏鍵盤的操作
InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(findViewById(R.id.fragment).getWindowToken(),0);//兩個參數
navController.navigateUp();
return super.onSupportNavigateUp();
}
- 運作後可以将自己添加的單詞釋義加入其中: