持续创作,加速生长!这是我参与「日新计划 6 月更文挑战」的第31天,点击检查活动概况
关于作者
众所周知,人生是一个漫长的流程,不断克服困难,不断反思行进的进程。在这个进程中会发生许多对于人生的质疑和考虑,所以我决议将自己的考虑,经验和故事悉数共享出来,以此寻找共识!!!
专心于Android/Unity和各种游戏开发技巧,以及各种资源共享(网站、工具、素材、源码、游戏等)
欢迎重视大众号【空名先生】获取更多资源和交流!
条件
这是小空坚持写的Android新手向系列,欢迎品味。
新手(√√√)
大佬(√)
实践进程
查找控件的几种方法
说这个之前咱们先谈论下有几种查找控件的方法:
- findViewById,没错。最原始陈旧也是最基本的拿到控件的方法,刚学Android的同学一般最早会的便是这个,兼容性好,适用于一切的场景,当你刚学Android或不知道其他计划的时分,运用findViewById肯定没错。除了代码冗余一点,书写费事一点(不过也能够在Android Studio插件商场找到主动写findViewById的插件)。
- ButterKnife,这是在官方findViewById之后最早出来的第三方结构,需求进行依靠,简化代码,提高效率,增加了代码的可读性,不过也并没有解决和findViewById相同的缺陷,而且作者现已三四年没更新,明确阐明现已抛弃。只不过不妨碍运用,尤其是老程序员以及老项目,不用不要紧,看到了一定要知道这是啥。github.com/JakeWharton…
- kotlin-android-extensions,不只要依靠三方库,而且只限于kotlin言语中运用,布局中写好控件,代码中能够直接引用,可是相同官方现已准备抛弃了,老项目见到要知道是啥就行。原理仍是findViewById。
- viewbinding和databinding,这是为了优化findViewById提出的另一个思路。经过视图绑定功用,就能够很轻松地实现与视图交互的代码。当启动后,体系会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的一切视图的直接引用。咱们今天这篇文主要就将这俩。
ViewBinding
开始实操前,咱们先了解她的优缺陷
长处
- 避免了控件空指针的过错,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”特点
<RelativeLayout
...
tools:viewBindingIgnore="true" >
...
</RelativeLayout>
敞开Bingding后,体系会运用驼峰命名法生成对应的绑定类,一Binding为后缀。
例如:布局activity_main.xml,生成的为ActivityMainBinding类
Activity中运用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/testName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
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中
/**
* Created by akitaka on 2022-06-25.
* @author akitaka
* @filename MyAdapter
* @describe
* @email 960576866@qq.com
*/
class MyAdapter (val params: List<MyModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
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中
/**
* Created by akitaka on 2022-06-25.
*
* @author akitaka
* @filename MyAdapter
* @describe
* @email 960576866@qq.com
*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private Context mContext;
private ArrayList<String> mData;
private final LayoutInflater inflater;
public MyAdapter(Context context, ArrayList<String> 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中运用
自界说View的布局文件叫【layout_my_view.xml】
private fun initView() {
//加载布局
val binding = LayoutMyViewBinding.inflate(LayoutInflater.from(context), this, true)
//binding.布局中的控件名即可
}
就这么简单。
在自界说View这有个需求特别提心的,比如横竖屏奇切换布局的时分
竖屏布局
<?xml version="1.0" encoding="utf-8"?>
<cn.zhima.OneView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/oneView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
横屏布局
<?xml version="1.0" encoding="utf-8"?>
<cn.zhima.TwoView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/twoView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
咱们知道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】
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvTesxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="芝麻粒儿" />
</RelativeLayout>
Activity布局:【activity_include.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".IncludeActivity">
<include
android:id="@+id/title"
layout="@layout/include_title" />
</LinearLayout>
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】
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Title"
android:textSize="20sp" />
</merge>
【activity_main.xml】
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/titlebar" />
</LinearLayout>
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类。
作者:小空和小芝中的小空
转载阐明-必须注明来历:芝麻粒儿 的个人主页 – 专栏 – ()
这位道友请留步☁️,我观你气度不凡,谈吐间隐约有王者霸气,日后定有一番大作为!!!周围有点赞保藏今天传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。