天天看點

制作單詞記錄App(一)制作單詞記錄App(一)

制作單詞記錄App(一)

  • 制作單詞記錄App(一)
    • 修正性能上的小bug
    • 開始制作新單詞界面

本文為學習類文檔,通過學習B站up主longway777的視訊,再加上自己的總結與了解的學習類文章,如有侵權,請聯系部落客進行删除

制作單詞記錄App(一)

這次制作的單詞記錄App是根據前面學習的Room元件,migrantion等

修正性能上的小bug

在之前建立的MyAdapter中,針對詞彙的點選跳轉界面和開關隐藏中文意思,我們将點選事件放在了onBindViewHolder中,這個函數會被系統在運作過程中多次調用,會造成性能上的重複開銷。

可以将其放在onCreateViewHolder中(建立一次即可)。

  1. 在onCreateViewHolder中定義itemView的ViewHolder對象
final MyViewHolder holder = new MyViewHolder(itemView);
           
  1. 調用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類型
    }
           
  1. 在onBindViewHolder中定義word的itemView的setTag()方法(友善使用者在create中使用getTag()方法擷取資料。)
//itemView的setTag方法可以擷取對象并在其他地方通過getTag擷取對應對象
        holder.itemView.setTag(R.id.word_for_view_holder,word); 
           
  1. 為了保證setTag在整個項目中保證全局唯一特性,設定int型參數,可以通過建立資源類将id存入以保證不沖突:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="word_for_view_holder" type="id"/>
</resources>
           
  1. 運作之後無變化,性能提高

開始制作新單詞界面

  1. 建立兩個fragement用來顯示添加界面和單詞顯示界面
  2. 建立資源類navigation檔案并連結兩個fragement(wordfragement、addfragement),建立兩者的動作聯系。(可以自己選擇制作進出動畫)
  3. 在Activity中删除之前建立的控件并導入NavHostFragement入口(在container控件中)完成系統連結
  4. 在wordfragement搭建界面,放入一個recyclerView
  5. 填寫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();
                }
            }
        });
    }
}
           
  1. 删除之前在MainActivity中的代碼并準備重寫MainActivity代碼(此處不該寫也能運作之前制作的界面,原因是将activity中的大部分代碼功能移植到了fragement中)
  2. 在wordsFragement中制作添加按鍵(使用自帶的FloatingActionButton)

    建立矢量圖示res->new->Vector Asset,選擇add圖示,背景色為白色,并建立。

    将FloatingActionButton拖動到界面中,并設定它的Layout_gravity(Bottom——true,CenterHorizontal——true)屬性和Layout_margin屬性

  3. 補充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);
            }
        });
    }
}
           
  1. 在導覽列設定傳回按鍵:
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();
    }
}

           
  1. 修改Fragement的Lable
  2. Add頁面的制作:
    制作單詞記錄App(一)制作單詞記錄App(一)
<?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>
           
  1. 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);//兩個參數
             }
         });
     }
 }
           
  1. 處理因鍵盤彈出而自動壓縮界面的操作:

    在清單檔案中設定activity的windowSoftInputMode=“adjustNothing”——不做任何調整:

<activity android:name="com.example.words.MainActivity"
            android:windowSoftInputMode="adjustNothing">
           
  1. 處理因點選傳回自設按鍵鍵盤不收回的設定:

    在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();
    }
           
  1. 運作後可以将自己添加的單詞釋義加入其中:
    制作單詞記錄App(一)制作單詞記錄App(一)
    制作單詞記錄App(一)制作單詞記錄App(一)