Android~ViewBinding使用學習

ViewBinding替換findviewbyid

1.有啥優點?

  • 比DataBinding輕量和快速,DataBinding中layout文件如果實現雙向綁定會改變原有佈局文件,業務和視圖關聯度高。
  • 更安全,kotlin擴展Synthetics也可直接通過id訪問控件,但是全局的會產生空指針錯誤。
  • 編譯安全,findviewbyid和ButterKnife會存在類型轉換問題,這一錯誤出現在運行時,而不是編譯時。產生的Binding類是根據layout文件生成,所以生成時就已經處理完成。

2. 使用

先更新AS到3.6及以上,在module的gradle中啓用viewBinding。
注: 如果你不想把XMLlayout文件中某個含有Id的view包含到生成類中的話,就設置此view的tools:viewBindingIgnore屬性爲true

android {
    ...
    viewBinding {
        enabled = true
    }
    ...
}

Activity中使用:

package com.viewbinding.test;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Color;
import android.os.Bundle;

import com.viewbinding.test.ui.main.MainFragment;

import com.viewbinding.test.databinding.MainActivityBinding;

public class MainActivity extends AppCompatActivity {

    private MainActivityBinding activityBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityBinding = MainActivityBinding.inflate(getLayoutInflater());
        setContentView(activityBinding.getRoot());
        activityBinding.container.setBackgroundColor(Color.DKGRAY);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.container, MainFragment.newInstance())
                    .commitNow();
        }
    }
}

Fragment中使用

package com.viewbinding.test.ui.main;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.viewbinding.test.databinding.MainFragmentBinding;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;

public class MainFragment extends Fragment {

    private MainFragmentBinding fragmentBinding;

    private MainViewModel mViewModel;

    public static MainFragment newInstance() {
        return new MainFragment();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        fragmentBinding = MainFragmentBinding.inflate(getLayoutInflater());
        return fragmentBinding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        fragmentBinding.message.setText("This is Test.");
        
        mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
    }
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        fragmentBinding = null; // 避免內存泄漏
    }
}

ViewHolder中使用:

static class ItemViewHolder extends RecyclerView.ViewHolder{
	private final ItemBinding binding;
	public BeautyViewHolder(@NonNull View itemView) {
	    super(itemView);
	    binding= ItemBinding.bind(itemView);
	}
}

3. 實現原理

我們可以通過分析編譯生成的中間文件MainFragmentBinding.java源碼,ViewBinding中重要的函數getRoot()、inflate()、bind(),可以在bind函數中看到熟悉的findviewbyid操作,谷歌在編譯的Gradle插件動了手腳,當開啓viewBinding後編譯時去掃描Layout文件自動生成Binding類。

// Generated by view binder compiler. Do not edit!
package com.viewbinding.test.databinding;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.viewbinding.ViewBinding;
import com.viewbinding.test.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;

public final class MainFragmentBinding implements ViewBinding {
  @NonNull
  private final ConstraintLayout rootView;

  @NonNull
  public final ConstraintLayout main;

  @NonNull
  public final TextView message;

  private MainFragmentBinding(@NonNull ConstraintLayout rootView, @NonNull ConstraintLayout main,
      @NonNull TextView message) {
    this.rootView = rootView;
    this.main = main;
    this.message = message;
  }

  @Override
  @NonNull
  public ConstraintLayout getRoot() {
    return rootView;
  }

  @NonNull
  public static MainFragmentBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, null, false);
  }

  @NonNull
  public static MainFragmentBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup parent, boolean attachToParent) {
    View root = inflater.inflate(R.layout.main_fragment, parent, false);
    if (attachToParent) {
      parent.addView(root);
    }
    return bind(root);
  }

  @NonNull
  public static MainFragmentBinding bind(@NonNull View rootView) {
    // The body of this method is generated in a way you would not otherwise write.
    // This is done to optimize the compiled bytecode for size and performance.
    String missingId;
    missingId: {
      ConstraintLayout main = rootView.findViewById(R.id.main);
      if (main == null) {
        missingId = "main";
        break missingId;
      }
      TextView message = rootView.findViewById(R.id.message);
      if (message == null) {
        missingId = "message";
        break missingId;
      }
      return new MainFragmentBinding((ConstraintLayout) rootView, main, message);
    }
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

參考:
1、你好, View Binding! 再次再見, findViewById!
2、是時候擁抱ViewBinding了!! (layout中使用include、merge標籤需要注意的點文中有詳細說明)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章