- 点击跳转=>《导航贴》- Unity手册,系统实战学习
- 点击跳转=>《导航贴》- Android手册,重温移动开发
关于作者本文约10千字,新手阅读需要14分钟,复习需要3分钟 【收藏随时查阅不再迷路】
前提众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣 !!!
专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎私我,交流群让学习不再孤单。
这是小空坚持写的Android新手向系列,欢迎品尝。
大佬(√)
新手(√√√)
实践过程 查找控件的几种方式说这个之前我们先谈论下有几种查找控件的方式:
- findViewById,没错。最原始古老也是最基本的拿到控件的方法,刚学Android的同学一般最先会的就是这个,兼容性好,适用于所有的场景,当你刚学Android或不知道其他方案的时候,使用findViewById绝对没错。除了代码冗余一点,书写麻烦一点(不过也可以在Android Studio插件市场找到自动写findViewById的插件)。
- ButterKnife,这是在官方findViewById之后最先出来的第三方框架,需要进行依赖,简化代码,提升效率,增加了代码的可读性,不过也并没有解决和findViewById一样的缺点,而且作者已经三四年没更新,明确说明已经废弃。只不过不妨碍使用,尤其是老程序员以及老项目,不用没关系,看到了一定要知道这是啥。https://github.com/JakeWharton/butterknife
- kotlin-android-extensions,不仅要依赖三方库,而且只限于kotlin语言中使用,布局中写好控件,代码中可以直接引用,但是同样官方已经准备废弃了,老项目见到要知道是啥就行。原理还是findViewById。
- viewbinding和databinding,这是为了优化findViewById提出的另一个思路。通过视图绑定功能,就可以很轻松地实现与视图交互的代码。当启动后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。我们今天这篇文主要就将这俩。
开始实操前,我们先了解她的优缺点
优点
- 避免了控件空指针的错误,findViewById中就可能造成空指针或类型转换错误。
- 代码可读性高
- 相比DataBinding,更加轻量级
缺点
- 会生成冗余的很多binding文件,虽然你不需要管理。
- 灵活性不高,如果需要动态切换布局,则多个布局必须根节点必须一致。
- 在 Activity、Fragment、Dialog、Adapter 中 ViewBinding 和 DataBinding 初始化方式有些不同
- 需要单独处理 include 带 merge 标签的布局,和不带 merge 标签的布局等等
配置说明
Android Studio 3.6 版本开始,就内置在 Gradle 插件中了,不需要添加任何额外的库来使用它们,但是在 Android Studio 3.6 和 Android Studio 4.0 中使用方式不一样。
Android Studio3.6以上
android {
viewBinding {
enabled = true
}
}
Android Studio4.0以上
android {
buildFeatures {
viewBinding = true
}
}
如果不想要某个布局文件生成binding,需要添加tools:viewBindingIgnore="true"属性
...
开启Bingding后,系统会利用驼峰命名法生成对应的绑定类,一Binding为后缀。
例如:布局activity_main.xml,生成的为ActivityMainBinding类
Java中
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.testName.setText("博客:芝麻粒儿");
}
}
Kotlin中
上面Java代码中ActivityMainBinding我们没有放到onCreate外面当作全局变量,而事实上,一个控件我们通常在多个地方使用,所以需要全局变量。Java没什么用特别的,在Kotlin中声明的变量都必须在声明的同时对其进行初始化。而这里我们显然无法在声明全局binding变量的同时对它进行初始化,所以这里又使用了lateinit关键字对binding变量进行了延迟初始化。
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.testName.text = "博客:芝麻粒儿"
}
}
Fragment中使用
需要注意的是为了有效保证binding变量的有效生命周期是在onCreateView()函数和onDestroyView()函数之间,除了在onCreateView创建,也要在onDestroyView及时置空。
Java中
public class MainFragment extends Fragment {
private FragmentMainBinding binding;
@Override
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentMainBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Kotlin中
class MainFragment : Fragment() {
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentMainBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Adapter中使用
创建一个名为【adapter_item.xml】的布局文件
Kotlin中
class MyAdapter (val params: List) : RecyclerView.Adapter () { inner class ViewHolder(binding: AdapterItemBinding) : RecyclerView.ViewHolder(binding.root) { val tvName: TextView = binding.fruitName } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = AdapterItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val paramsTemp = params[position] holder.tvName.text = paramsTemp.name } override fun getItemCount() = MyModel.size }
Java中
public class MyAdapter extends RecyclerView.Adapter自定义View中使用{ private Context mContext; private ArrayList mData; private final LayoutInflater inflater; public MyAdapter(Context context, ArrayList data) { mContext = context; mData = data; inflater = LayoutInflater.from(context); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { AdapterItemBinding itemBinding = AdapterItemBinding.inflate(inflater, parent, false); return new ViewHolder(itemBinding); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.textView.setText(mData.get(position)); } @Override public int getItemCount() { return mData.size(); } static class ViewHolder extends RecyclerView.ViewHolder { TextView textView; public ViewHolder(@NonNull AdapterItemBinding itemBinding) { super(itemBinding.getRoot()); textView = itemBinding.textView; } } }
自定义View的布局文件叫【layout_my_view.xml】
private fun initView() {
//加载布局
val binding = LayoutMyViewBinding.inflate(LayoutInflater.from(context), this, true)
//binding.布局中的控件名即可
}
就这么简单。
在自定义View这有个需要特别提心的,比如横竖屏奇切换布局的时候
竖屏布局
横屏布局
我们知道binding会根据文件名利用驼峰命名来实现bind类,上面的横竖屏布局文件文件名相同,再切换引用的时候就会出现根元素的 id不一致的报错信息。
Configurations for activity_main.xml must agree on the root element’s ID.
所以我们需要为这两个布局额外嵌套一层相同的ViewGroup,比如RelativeLayout或LinearLayout。之后就可以正常运行了。
Dialog中使用Dialog dialog = new Dialog(getContext());
DialogTipBinding dialogTipBinding = DialogTipBinding.inflate(LayoutInflater.from(getContext()));
dialog.setContentView(dialogTipBinding.getRoot());
dialogTipBinding.tvTip.setText("弹框提示");
dialog.show();
include和merge标签的使用
布局中可以嵌套include标签,但是如果查找id的话,如果引入的这个布局根不是merge的话则必须include标签需要加上个id。
布局【include_title.xml】
Activity布局:【activity_include.xml】
public class IncludeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.title.tvTesxt.setText("空名先生");
}
}
merge和include最大的区别在于,使用merge标签引入的布局在某些情况下可以减少一层布局的嵌套,而更少的布局嵌套通常代表着更高的运行效率。
【titlebar.xml】
【activity_main.xml】
Kotlin的MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var titlebarBinding: TitlebarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
titlebarBinding = TitlebarBinding.bind(binding.root)
setContentView(binding.root)
titlebarBinding.title.text = "芝麻粒儿"
}
}
在onCreate()函数中可以看到,这里我们又定义了一个titlebarBinding变量。不用说大佬们也看出来了,这个TitlebarBinding就是Android Studio根据我们的titlebar.xml布局文件自动生成的Binding类。
其他作者:小空和小芝中的小空
转载说明-务必注明来源:https://zhima.blog.csdn.net/
这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气,日后定有一番大作为!!!旁边有点赞收藏今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。
温馨提示:点击下方卡片获取更多意想不到的资源。



