Android框架 使用ViewBinding和ButterKnife

ButterKnife 10.2.3 Github 网站:
https://github.com/JakeWharton/butterknife

进入 github 网站就可以看到:
Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only critical bug fixes for integration with AGP will be considered. Feature development and general bug fixes have stopped.

翻译:
现在不建议使用此工具。 请使用 viewBinding 。很明显,现有版本将继续起作用,但仅考虑与AGP集成的关键错误修复。 功能开发和常规错误修复已停止。

可以知道现在 ButterKnife 已经被 google 的 viewBinding 替代了。

一、 使用 ButterKnife

1. gradle 文件配置

gradle Modul

plugins {
    id 'com.jakewharton.butterknife'
}

android {
  ...
  // Butterknife requires Java 8.
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  implementation 'com.jakewharton:butterknife:10.2.3'
  annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
}

配置 gradle Project

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
    }
}
2. 使用注解来绑定资源

@BindView、@OnClick、 @BindString

public class MainActivity extends AppCompatActivity {
    //绑定View
    @BindView(R2.id.textView) TextView textView;
    @BindView(R2.id.button) Button button;
    @BindView(R2.id.button2) Button button2;

    //绑定String
    @BindString(R2.string.test_butter_knife) String str;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //github官方建议使用R2,所以资源有修改时要build项目
        ButterKnife.bind(this);

        textView.setText("测试 ButterKnife");
        button.setText("btn1");
        button2.setText("btn2");
    }

    //这里有点类似于:android:onClick="btnClick2"
    @OnClick({R2.id.button, R2.id.button2})
    public void btnClick(View view) {
        switch (view.getId()) {
            case R.id.button:
                Toast.makeText(this, "btn1", Toast.LENGTH_SHORT).show();
                break;
            case R.id.button2:
                Toast.makeText(this, "btn2", Toast.LENGTH_SHORT).show();
                break;
        }
    }

    public void btnClick2(View view) {
        switch (view.getId()) {
            case R.id.button:
                Toast.makeText(this, "btn1", Toast.LENGTH_SHORT).show();
                break;
            case R.id.button2:
                Toast.makeText(this, "btn2", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}
3. 注入 ListView

发现其实也没多大的区别。

ListViewActivity.java

public class ListViewActivity extends AppCompatActivity {

    //绑定ListView
    @BindView(R2.id.list_view)
    ListView listView;

    //数据源
    private final String[] string = {"btn1", "btn2"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);

        ButterKnife.bind(this);

        listView.setAdapter(new ListViewAdapter(this, string));

        //设置点击事件
//        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
//            @Override
//            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//                Toast.makeText(ListViewActivity.this, string[position], Toast.LENGTH_SHORT).show();
//            }
//        });
    }

    //使用ButterKnife绑定点击事件
    @OnItemClick(R2.id.list_view)
    //进入clickListViewItem方法查看,会发现onItemClick方法会根据参数类型自动匹配
    public void clickListViewItem(int position) {
        Toast.makeText(ListViewActivity.this, string[position], Toast.LENGTH_SHORT).show();
    }
}

ListViewAdapter.java

public class ListViewAdapter extends ArrayAdapter {

    private final Context mContext;
    private final String[] mStr;

    public ListViewAdapter(@NonNull Context context, @NonNull Object[] objects) {
        //自己设定资源convertView
        super(context, -1, objects);
        mContext = context;
        mStr= (String[]) objects;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        ViewHolder holder = null;
        if (convertView == null) {
             convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view_item, parent, false);
            holder = new ViewHolder(convertView);
            //holder.textView = convertView.findViewById(R.id.tv_item);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.textView.setText(mStr[position]);
        return convertView;
    }

    static class ViewHolder {
        @BindView(R2.id.tv_item) TextView textView;

        public ViewHolder(View view) {
            ButterKnife.bind(this, view);
        }
    }
}
4. 注入 RecyclerView

RecyclerView 的使用流程参考:
https://blog.csdn.net/Blue3Red1/article/details/111673092

记得导入依赖文件

implementation 'androidx.recyclerview:recyclerview:1.1.0'

RecyclerViewActivity.java

public class RecyclerViewActivity extends AppCompatActivity {

    private final String[] strings = {"btn1", "btn2"};

    @BindView(R2.id.recycler_view) RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);
        ButterKnife.bind(this);

        RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(this, strings);

        recyclerView.setAdapter(mAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        mAdapter.setOnItemClickListener( position -> {
            Toast.makeText(this, strings[position], Toast.LENGTH_SHORT).show();
        });
    }
}

RecyclerViewAdapter.java

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {

    private final Context mContext;
    private final String[] mData;

    private OnItemClickListener onItemClickListener;

    public RecyclerViewAdapter(Context context, String[] strings) {
        mContext = context;
        mData = strings;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.list_view_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.textView.setText(mData[position]);
        holder.textView.setOnClickListener(v -> {
            this.onItemClickListener.setItemClick(position);
        });
    }

    @Override
    public int getItemCount() {
        return mData.length;
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {
        @BindView(R2.id.tv_item)
        TextView textView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    interface OnItemClickListener {
        void setItemClick(int position);
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }
}

发现使用了注解来绑定视图后,会出现以上的警告,其实也就是上面说的 ButterKnife 已经被弃用了,请使用 viewBinding.
在这里插入图片描述
以前还可以配合插件 ButterKnifeZelezny. 不过这玩意已经凉了,就不推荐使用了。

二、 视图绑定 viewBinding

视图绑定:https://developer.android.com/topic/libraries/view-binding#java
https://medium.com/androiddevelopers/use-view-binding-to-replace-findviewbyid-c83942471fc

该功能需要在 Android Studio 3.6 Canary 11 及更高版本中才可用。
在大多数情况下,视图绑定会替代 findViewById。

1. 在 gradle Module 中配置:
android {
        //in Android Studio 4.0
        buildFeatures {
        	viewBinding = true
   		}	
}
2. 使用方法:

activity_main.xml

<TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

tools:viewBindingIgnore="true" //生成绑定类时忽略某个布局文件

为某个模块启用视图绑定功能后,系统会为该模块中包含的每个 XML 布局文件生成一个绑定类。
例如:activity_main.xml就会生成为 ActivityMainBinding

Activity 中使用

private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        
        setContentView(view);//getBinding在setContentView之前

		//binding.name 对应 xml TextView 的 ID
		binding.name.setText("viewBinding测试");
    }

Fragment 中使用

    private ActivityMainBinding binding;

    @Override
    public View onCreateView (LayoutInflater inflater,
                              ViewGroup container,
                              Bundle savedInstanceState) {
        binding = ActivityMainBinding.inflate(inflater, container, false);
        View view = binding.getRoot();
        return view;
    }
	//inflate() 方法会要求您传入布局膨胀器。如果布局已膨胀,您可以调用绑定类的静态 bind() 方法。
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
    
3. 注入 RecyclerView

以上 ListView RecyclerView 中使用 ButterKnife 的视图,可以使用 ViewBinding 来替代。

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> {

    private final Context mContext;

    public MovieAdapter(Context mContext) {
        this.mContext = mContext;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        MovieDoubleItemBinding doubleItemBinding = MovieDoubleItemBinding
                .inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new MyViewHolder(doubleItemBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
		List<Result.MoviesBean> movies = mData.getMovies().get(position);
        holder.bindFilmName(movies.getTitle());
    }

    @Override
    public int getItemCount() {
        return mData.getCount()-1;
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {
    
		private MovieDoubleItemBinding doubleItemBinding;
		private TextView filmName;
		 
        public MyViewHolder(MovieDoubleItemBinding doubleItemBinding) {
            super(doubleItemBinding.getRoot());
         	this.doubleItemBinding = doubleItemBinding;
         	filmName = doubleItemBinding.tvFilmName;
        }
		
		public void bindFilmName(String s) {
            filmName.setText(s);
        }
    }
}
4. 与 findViewById 的区别

在这里插入图片描述
与使用 findViewById 相比,视图绑定具有一些很显著的优点:

  • Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
  • 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。

这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。

5. 与数据绑定的对比

视图绑定和数据绑定均会生成可用于直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,与数据绑定相比,具有以下优势:

  • 更快的编译速度: 视图绑定不需要处理注释,因此编译时间更短。
  • 易于使用: 视图绑定不需要特别标记的 XML 布局文件,因此在应用中采用速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。

反过来,与数据绑定相比,视图绑定也具有以下限制:

  • 视图绑定不支持布局变量或布局表达式,因此不能用于直接在 XML 布局文件中声明动态界面内容。
  • 视图绑定不支持双向数据绑定。

考虑到这些因素,在某些情况下,最好在项目中同时使用视图绑定和数据绑定。您可以在需要高级功能的布局中使用数据绑定,而在不需要高级功能的布局中使用视图绑定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值