制作单词记录App(一)
本文为学习类文档,通过学习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();
}
- 运行后可以将自己添加的单词释义加入其中: