1. 程式人生 > 實用技巧 >中級實訓Android學習記錄——RecyclerView、Android儲存

中級實訓Android學習記錄——RecyclerView、Android儲存

學習記錄 2020/11/26

RecyclerView

  • RecyclerView

RecyclerView在之前就學習過,但用的時候才發現沒有學習記錄,所以在這裡自行總結。

使用RecyclerView建立列表檢視的步驟:

  1. 建立一個activity(Android studio會幫你順便宣告並建立layout),在activity的xml檔案中建立一個RecyclerView

    // 我們在activity_linear_recycler.xml中宣告一個RecyclerView
        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:id="@+id/rv_main"
            android:background="@color/purple_700"/>
    
  2. 建立一個列表的一個小項所用的layout

    // 這裡我們建立的是layout_linear_item.xml,裡面只有一個簡單的TextView
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textColor="#000"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
  3. 建立使用小項layout的RecyclerViewAdapter

    public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {
    // 自己建立一個ViewHolder並將它傳入Adapter的模板引數中
        private Context mContext;
    
        public  LinearAdapter(Context context) {
            this.mContext = context;
        }
    
        
        // 注意這裡的返回值也要改成自己的ViewHolder
        @NonNull
        @Override
        public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
        }
    
        // 注意這裡傳入的引數也要改成自己的ViewHolder
        @Override
        public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, int position) {
            holder.textView.setText("Hello World!");
        }
    
        // 自定義一個列表的小項的個數,一般我們可以使用一個列表來宣告
        @Override
        public int getItemCount() {
            return 30;
        }
    
        // 建立一個ViewHolder來自定義小項
        class LinearViewHolder extends  RecyclerView.ViewHolder {
    
            private TextView textView;
    
            public LinearViewHolder(@NonNull View itemView) {
                super(itemView);
                textView = itemView.findViewById(R.id.tv_title);
            }
        }
    
    }
    
  4. 最後就可以在Activity中直接使用RecyclerView

// 先宣告一個變數 private RecyclerView mRvMain;
        mRvMain = findViewById(R.id.rv_main);
        mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerActivity.this));
       
        mRvMain.setAdapter(new LinearAdapter(LinearRecyclerActivity.this));
  1. 加每個小項之間的分隔線的方法

    1. 在Activity.java中自定義一個類並繼承ItemDecoration

      class myDecoration extends RecyclerView.ItemDecoration {
              @Override
              public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                  super.getItemOffsets(outRect, view, parent, state);
                  outRect.set(0, 0, 0, getResources().getDimensionPixelOffset(R.dimen.dividerHeight));
              }
          }
      // 這裡的set函式的四個引數代表的方向依次是左、上、右、下
      // 所以我們的效果其實是在每個item的下方加一個R.dimen.dividerHeight大小的橫線
      
    2. 呼叫RecyclerView的addItemDecoration方法即可

    mRvMain.addItemDecoration(new myDecoration());
    

Android 儲存

  • SharedPreferences 輕量級資料儲存

    • 一般儲存形式表現為以Key-Value的形式,儲存在XML檔案中
    • 用SharedPreferences來讀取xml檔案
    • 用SharedPreferences.Editor來更改xml檔案
  • SharedPreferences 用法

    • 獲取資料

    • private SharedPreferences mSharedPreferences;
      mSharedPreferences = getSharedPreferences("data", MODE_PRIVATE); // 這裡第一個引數表示檔名,第二個引數表示檔案開啟的模式,一般使用MODE_PRIVATE
      
      // 假設已經使用Editor輸入資料,key="name"
      
      String data = mSharedPreferences.getString("name", "");
      
    • 輸入資料

    • private SharedPreferences.Editor mEditor;
      mEditor = mSharedPreferences.edit();
      //呼叫put型別的方法來輸入key-value資料
      mEditor.putString("name", "haha")
      // 呼叫apply或者commit進行資料更新
       // apply是非同步
       // commit是同步
      mEditor.apply(); 
      
    • SharedPreferences的儲存目錄

      • /data/data//shared_prefs
    • SharedPreferences的儲存目錄的檢視方式

    • 命令列下

    • // 使用android的shell
      adb shell
      // 呼叫run-as 後面加上自己的完整applicaiton的名稱
      run-as com.skypan.helloworld
      // 進入目錄shared_prefs
      cd shared_prefs
      // 在命令列中列出data.xml的內容
      cat data.xml
      
  • 儲存概念

    • Android儲存分為
      • 內部儲存(Internal Storage)
        • 內部儲存會隨應用的解除安裝而刪除,如
          • /data/data//shared_prefs
          • /data/data//databases
          • /data/data//files
          • /data/data//cache
        • 利用的方法可以是
          • context.getFilesDir()獲得目錄/data/data//files
          • context.getCacheDir()獲得目錄/data/data//cache
      • 外部儲存(External Storage)
        • 公有目錄
          • 公有目錄的獲取
            • Environment.getExternalStoragePublicDirectory(int type)
            • type可以是Environment下的已經定義好的變數
        • 私有目錄,隨應用的解除安裝而刪除,如
          • /mnt/sdcard/Anroid/data/data//cache
          • /mnt/sdcard/Anroid/data/data//files
  • File內部儲存

    • 使用的類

    • FileOutputStream,FileInputStream

    • 儲存資料

      private void save(String content) {
      	try {
           FileOutputStream fileOutputStream = openFileOutput("test.txt", MODE_PRIVATE); // 第一個引數代表檔名,第二個引數是檔案讀寫方式
           fileOutputStream.write(content.getBytes());
           // 這裡呼叫的write函式接受的是byte[],所以需要將String物件轉換成byte
       } catch (IOException e) {
           // 注意,此時其實丟出的不止是IOException,還有FileNotFoundException,不過這個錯誤繼承了IOException,所以都可以被IOException catch到
           e.printStackTrace();
       } finally { // 無論如何,我們都需要關閉一開始開啟的檔案
           if (fileOutputStream != null) { 
               // 如果已經打開了,就關閉
               try {
                   fileOutputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
      }
      

      讀取資料

      private String read() {
       FileInputStream fileInputStream = null;
       try {
           FileInputStream fileInputStream = openFileInput("test.txt");
           byte[] buff = new byte[1024];
           StringBuilder sb = new StringBuilder("");
           int len = 0;
           while((len = fileInputStream.read(buff)) > 0) {
               sb.append(new String(buff, 0, len));
               // 從buff的0開始,將len長度的byte做成String加到sb的後面
           }
           return sb.toString();
       } catch (IOException e) {
           e.printStackTrace();
       
           // 預設可能發生錯誤,直接返回空
           return null;
      }
      
  • File外部儲存

    • 使用的類

    • FileOutputStream,FileInputStream,Environment

    • 儲存資料

      private void save(String content) {
      	try {
           File dir = new File(Environment.getExternalStorageDirectory(), "skypan");
      // 第一個是找到外部儲存目錄,第二個引數是資料夾的名字
           if (!dir.exists()) {
               dir.mkdirs();
               // 使用mkdirs而非mkdir是因為他會幫我們建立一系列的資料夾(當我們的資料夾路徑很複雜的時候)
           }
           File file = new File(dir, "test.txt");
           if (!file.exists()) {
               file.createNewFile();
           }
           FileOutputStream fileOutputStream = new FileOutputStream(file);
           
           fileOutputStream.write(content.getBytes());
           // 這裡呼叫的write函式接受的是byte[],所以需要將String物件轉換成byte
       } catch (IOException e) {
           // 注意,此時其實丟出的不止是IOException,還有FileNotFoundException,不過這個錯誤繼承了IOException,所以都可以被IOException catch到
           e.printStackTrace();
       } finally { // 無論如何,我們都需要關閉一開始開啟的檔案
           if (fileOutputStream != null) { 
               // 如果已經打開了,就關閉
               try {
                   fileOutputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
      }
      

      讀取資料

      private String read() {
       FileInputStream fileInputStream = null;
       try {
           File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"skypan", "test.txt");
           // 第一個引數是我們的資料夾的絕對路徑,其中File.separator的值="/",第二個引數是我們的檔名
           fileInputStream = new FileInputStream(file);
           byte[] buff = new byte[1024];
           StringBuilder sb = new StringBuilder("");
           int len = 0;
           while((len = fileInputStream.read(buff)) > 0) {
               sb.append(new String(buff, 0, len));
               // 從buff的0開始,將len長度的byte做成String加到sb的後面
           }
           return sb.toString();
       } catch (IOException e) {
           e.printStackTrace();
       
           // 預設可能發生錯誤,直接返回空
           return null;
      }
      

      注意,並不能直接使用以上程式碼,在使用外部儲存之前,我們需要先獲取外部儲存的讀寫許可權

      1. 在AndroidManifest.xml中宣告以下語句

        <user-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        
      2. 如果在build.gradle中,我們的sdk版本超過了23,我們還需要動態申請許可權

        在build.gradle中檢視sdk版本

        compileSdkVersion n
        

        如果$n\geq 23$,就需要動態申請許可權,在使用外部儲存之前:

        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WIRTE_EXTERNAL_STORAGE}, 1);
        // 第一個引數是呼叫這個函式的activity(context),第二個引數是申請的是什麼許可權,第三個是許可權返回結果的識別碼,如果我們需要得到使用者是否確認給予許可權,我們需要額外呼叫方法進行判斷
        

      在申請了許可權之後,就可以正常的執行之前說的使用外部儲存的程式碼了。