Android官方数据绑定框架DataBinding(一)
还记得在博客《高逼格UI-ASD(Android Support Design)》的開始曾经说过,Android最新推出了一个官方的数据绑定框架-Data Binding Library。
如今github上也有非常多三方的数据绑定框架,可是我们为什么要选择官方的呢?恩。答对了。就是由于是官方的,三方的东西说不定什么时候作者一步高兴就停止更新了,官方的就不一样了,我们能够看到它渐渐的稳定起来。
好了废话不多说。从这篇博客開始,我们就来了解一下android最新给我们带来的数据绑定框架——Data Binding Library。
数据绑定框架给我们带来了更大的方便性。曾经我们可能须要在Activity
里写非常多的findViewById
。烦人的代码也添加了我们代码的耦合性,如今我们立即就能够抛弃那么多的findViewById
。讲到这里,有人可能会有个疑问:我使用一些注解框架也能够不用findViewById
啊,是的,可是注解注定要拖慢我们代码的速度,Data Binding则不会,官网文档说还会提高解析XML的速度,最基本的Data Binding并非单单降低了我们的findViewById
,很多其它优点请往下看文章。
一、环境
在開始使用新东西之前,我们须要略微的配置一下环境。这里要求你的Android Studio版本号是1.3+,使用eclipse的同学临时还没有办法使用该框架,请换用Android Studio。
还有。在開始之前,请更新你的Support repository
到最新的版本号。
万事俱备。那我们就開始搭配环境!
新建一个project
,在dependencies
中加入以下依赖
classpath "com.android.databinding:dataBinder:1.0-rc1"
新建module
,并且在module
的build.gradle文件里加入
apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'
ok,到如今为止,我们的环境就准备完毕了,以下我们就開始Data Binding的学习啦。
二、Data Binding尝试
在代码開始,我们并不直接进入新东西的解说,并且以一段代码展现Data Binding的魅力。
首先我们须要一个java bean
,非常easy,一个学生类。
public class Student {
private String name;
private String addr;
public Student() {
}
public Student(String name, String addr) {
this.name = name;
this.addr = addr;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return this.addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
再来看看我们布局文件怎么写:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="stu"
type="org.loader.androiddatabinding.Student" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.addr}"/>
</LinearLayout>
</layout>
能够看到我们的xml布局和曾经还有有一定的区别的。可是区别也不是非常大。
最后来看看Activity
怎么写。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setStu(new Student("loader", "山东莱芜"));
}
}
Activity
的代码非常easy,就加入了两行代码,并且,值得注意的是:我们并没有findViewById
然后再去setText
。
这段小代码执行的结果大家可能已经猜到了,就是在界面上显示loader
和山东莱芜
两句话。
)
在看完小实例后,大家是不是感觉棒棒哒? 没有了之前的find控件,没有了setText。Activity
代码更加简洁明了!
以下開始。我们进入Data Binding的学习!
三、 初始Data Binding
上面的代码算是带领我们进入了Data Binding的世界。那我们先从布局文件開始入手Data Binding吧。再来看看上面的布局文件。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="stu"
type="org.loader.androiddatabinding.Student" />
</data>
...
</layout>
我们的根节点变成了layout
,在layout
的子节点中分成两部分,第一部分是data
节点,第二部分才是我们之前的根节点。在data
节点下我们又定义了一个variable
。
从名称上看,这应该是一个变量,变量的名称是stu
。类型是org.loader.androiddatabinding.Student
,这相似我们在java文件里这么定义:
org.loader.androiddatabinding.Student stu;
ok。这样非常好理解了吧。只是这里要写Student
完整的包名,一个还好,假设这里我们须要多个Student
呢?要累死? NO,NO,NO,我们还能够向写java文件那样导入包。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="org.loader.app2.Student" />
<variable
name="stu"
type="Student" />
</data>
...
</layout>
这样写。就相似于java的
import org.loader.app2.Student;
...
Student stu;
...
既然变量我们定义好了。那该怎么使用呢?还是看上面的xml文件。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
...
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.addr}"/>
</LinearLayout>
</layout>
恩,注意看两个TextView
的android:text
。它的值是一个以@
開始。以{}包裹的形式出现。而内容呢?是stu.name
。
stu就是我们上面定义的variable
,
name还记得吗?是我们Student
类中的一个变量。事实上这里就会去调用stu.getName()
方法。
好了。非常快。我们就入门了Data Binding,以下让我们来多定义几个变量试试看。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="org.loader.app2.Student" />
<variable
name="stu"
type="Student" />
<variable
name="str"
type="String"/>
<variable
name="error"
type="boolean"/>
<variable
name="num"
type="int" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{str}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(num)}"/>
</LinearLayout>
</layout>
来看看定义的变量,多了好几个,有一个String
类型的变量我们并没有导包,这里说明一下,和在java里一样,java.lang
包里的类,我们是能够不用导包的。再往下,一个boolean
和int
类型的变量,都是java基本类型的。所以说嘛,在这里定义变量。你就想成是在java里定义就ok。
再来看看这几个TextView
。第二个,我们直接使用@{str}
来为android:text
设置成上面定义个str
的值,继续往下要注意了,我们使用了
android:text="@{String.valueOf(num)}"
来设置了一个int
类型的变量。大家都知道我们在给android:text
设置int
类型的值时一定要转化为String
类型,要不它就觉得是资源文件了,这里我们还学到了一点,在xml中,我们不仅能够使用变量。并且还能够调用方法。
四、 变量定义的高级部分
在上面,我们学会了怎样去在xml中定义变量。可是不知道你发现没?我们未定义像List
、Map
等这种集合变量。那究竟能不能定义呢?答案肯定是能够的。并且定义的方式和我们上面的基本一致,区别就在于我们还须要为它定义key的变量,比如:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="org.loader.app2.Student" />
<import type="android.graphics.Bitmap" />
<import type="java.util.ArrayList" />
<import type="java.util.HashMap" />
<variable
name="stu"
type="Student" />
<variable
name="str"
type="String"/>
<variable
name="error"
type="boolean"/>
<variable
name="num"
type="int" />
<variable
name="list"
type="ArrayList<String>" />
<variable
name="map"
type="HashMap<String, String>" />
<variable
name="array"
type="String[]" />
<variable
name="listKey"
type="int" />
<variable
name="mapKey"
type="String" />
<variable
name="arrayKey"
type="int" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{stu.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{str}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(num)}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{list[listKey]}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{map[`name`]}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{array[0]}"/>
</LinearLayout>
</layout>
这段代码比較长。可是我们仅关心那几个集合和数组,能够看到我们定义集合和定义普通变量一样。仅仅只是这里我们还指定了一些的泛型,比如:ArrayList<String>
。
以下我们还为以下使用这些集合准备了几个key,也都是变量。
继续看看怎么使用,和我们在java中使用不同,这里都是以:集合变量名[key]的形式使用,假设你的key是一个字面字符串能够使用反引號,也能够使用转义后的双引號。恩,这里也没有什么能够说的了。大家多看几遍就掌握了,都是概念性的东西,记住就ok。
五、在java代码中使用
前面定义了这么多变量。可是我们还没有给他们赋值。在哪赋值呢?肯定是在java代码中使用了,大部分情况我们还是在Activity
中去使用它,曾经我们都是在onCreate
方法中通过setContentView
去设置布局,但如今不一样了。如今我们是用过DataBindingUtil
类的一个静态方法setContentView
设置布局。同一时候该方法会返回一个对象。什么对象?这个对象有点特殊,它是一个自己主动生成的类的对象,看以下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
}
看到ActivityMainBinding
了吗?就是它!那自己主动生成有什么规则了没?当然有了,记好了
将我们布局文件的首字母大写,并且去掉下划线,将下划线后面的字母大写,加上Binding组成。
看看上面的类。是不是符合这个规则。
继续看看这个对象哪来的,是通过
DataBindingUtil.setContentView(this, R.layout.activity_main);
返回的,DataBindingUtil.setContentView的两个參数各自是当前Activity
和布局文件。
那接下来,就是我们关心的给变量赋值了。
@Override
protected void onCreate(Bundle savedInstanceState) {
...
binding.setStu(new Student("loader"));
binding.setStr("string");
binding.setError(false);
ArrayList<String> list = new ArrayList<String>() {
{
add("arraylist");
}
};
binding.setList(list);
binding.setListKey(0);
HashMap<String, String> map = new HashMap<String, String>() {
{
put("name", "hashmap");
}
};
binding.setMap(map);
// binding.setMapKey("name");
String[] array = new String[1];
array[0] = "array";
binding.setArray(array);
binding.setArrayKey(0);
}
一连串的binding.setXXX。这个XXX是什么呢?就是我们在xml中定义的那些变量首字母大写了!
也没好好说的吧。多看几遍。
六、 表达式
短暂的幸福时光,我们还是要告别java代码了。继续回到xml中。这一块,我们来学习一下表达式,什么?这玩意在xml中还支持表达式!
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{error ? "error" : "ok"}'/>
还记得上面我们定义了一个boolean的变量没实用到。这里我们就用到了,看好android:text
,这里是一个三元表达式,假设error是true,则text就是error,否则是ok。
这里还支持null合并操作,什么是null合并,相信看一眼你就知道了
android:text='@{str==null ?? "not null"}'
简单解释一下,假设str是null,text的值就是str本身,否则就是”not null”。
它还支持一下表达式:
- Mathematical + - / * %
- String concatenation +
- Logical && ||
- Binary & | ^
- Unary + - ! ~
- Shift >> >>> <<
- Comparison == > < >= <=
- instanceof
- Grouping ()
- Literals - character, String, numeric, null
- Cast
- Method calls
- Field access
- Array access []
- Ternary operator ?
:
可是它不支持一下表达式:
- this
- super
- new
- Explicit generic invocation
七、 其它遗漏点
讲到这里,xml中的事情基本算完了,可是还有几个小地方没有说,顺便说一下。
1. 设置别名
假如我们import了两个同样名称的类咋办?别怕,别名来解救你!
比如:
...
<data>
<import type="xxx.Name" alias="MyName">
<import type="xxx.xx.Name">
</data>
<TextView xxx:@{MyName.getName()}>
<TextView xxx:@{Name.getName()}>
...
- 自己定义Binding名称
还记得系统为我们生成好的那个binding类名吗?假设仅仅能使用那样的是不是有点太暴力了?好在google对我们还算友好了,同意我们自己定义binding名称,定制名称也非常easy。就是给data一个class字段就ok。比如:
<data class=".Custom">
...
</data>
那么:DataBindingUtils.setContentView返回的binding类就是:你的应用包名.Custom
。
八、事件绑定
大家都知道。在xml中我们能够给button
设置一个onClick
来达到事件的绑定。如今DataBinding也提供了事件绑定。并且不仅仅是button
。
来看一下:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="org.loader.app3.EventHandlers" />
<variable
name="handlers"
type="EventHandlers" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLICK ME"
android:onClick="@{handlers.handleClick}"/>
</LinearLayout>
</layout>
定义了一个EventHandlers
类型的handlers
变量,并在onClick的时候执行EventHandlers
的handleClick
方法。
继续看看EventHandlers是怎么写的。
public class EventHandlers {
public void handleClick(View view) {
Toast.makeText(view.getContext(), "you clicked the view", Toast.LENGTH_LONG).show();
}
}
非常easy,就是简单的Toast
了一下,这里要注意的是。handlerClick
方法须要一个View
的參数。
九、 数据对象
我们学会了通过binding为我们的变量设置数据,可是不知道你有没有发现一个问题,当我们数据改变的时候会怎样?数据是尾随着改变呢?还是原来的数据呢?这里告诉你答案:非常不幸。显示的还是原来的数据?那有没有办法让数据源发生变化后显示的数据也随之发生变化?先来想想ListView
是怎么做的。 ListView
的数据是通过Adapter
提供的,当数据发生改变时,我们通过notifyDatasetChanged
通过UI去改变数据。这里面的原理事实上就是内容观察者,庆幸的是DataBinding也支持内容观察者,并且使用起来也相当方便!
BaseObservable
我们能够通过Observable的方式去通知UI数据已经改变了,当然了。官方为我们提供了更加简便的方式BaseObservable
,我们的实体类仅仅须要继承该类,稍做几个操作。就能轻松实现数据变化的通知。怎样使用呢? 首先我们的实体类要继承BaseObservale
类。第二步在Getter
上使用注解@Bindable
。第三步,在Setter
里调用方法notifyPropertyChanged
,第四步,完毕。
就是这么简单。以下我们来实际操作一下。
首先定义一个实体类,并继承BaseObservable
public class Student extends BaseObservable {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(org.loader.app4.BR.name);
}
}
观察getName方法,我们使用了@Bindable
注解,观察setName,我们调用了notifyPropertyChanged
方法,这种方法还须要一个參数。这里參数相似于R.java
,保存了我们全部变量的引用地址,这里我们使用了name。
再来看看布局文件。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class=".Custom">
<import type="org.loader.app4.Student" />
<variable
name="stu"
type="Student"/>
<variable
name="click"
type="org.loader.app4.MainActivity" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{click.click}"
android:text="@{stu.name}"/>
</layout>
不多说了。我们给TextView
设置了文本。还有点击事件。
Activity,
public class MainActivity extends AppCompatActivity {
private Student mStu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
org.loader.app4.Custom binding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
mStu = new Student("loader");
binding.setStu(mStu);
binding.setClick(this);
}
public void click(View view) {
mStu.setName("qibin");
}
}
这段代码,首先显示的是loader,当我们点击TextView
时。界面换成qibin。
ObservableFields家族
上面使用BaseObservable
已经非常easy了,可是google工程师还不满足,继续给我们封装了一系列的ObservableFields
。这里有ObservableField
。ObservableBoolean
,ObservableByte
,ObservableChar
,ObservableShort
,ObservableInt
,ObservableLong
,ObservableFloat
,ObservableDouble
,ObservableParcelable
ObservableFields的用法就更加简单了,比如以下代码。
public class People {
public ObservableField<String> name = new ObservableField<>();
public ObservableInt age = new ObservableInt();
public ObservableBoolean isMan = new ObservableBoolean();
}
非常easy,仅仅有三个ObservableField变量。并且没有getter和setter,由于我们不须要getter和setter。
在xml中怎么使用呢?
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class=".Custom">
<variable
name="people"
type="org.loader.app4.People" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{people.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(people.age)}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{people.isMan ? "man" : "women"}'/>
</LinearLayout>
</layout>
也非常easy,直接使用变量。那怎么赋值和取值呢?这些ObservableField都会有一对get
和set
方法。所以使用起来也非常方便了:
...
mPeople = new People();
binding.setPeople(mPeople);
mPeople.name.set("people");
mPeople.age.set(19);
mPeople.isMan.set(true);
...
也不多说了。
Observable Collections
既然普通的变量我们有了ObservableFields的分装,那集合呢?当然也有啦。来看着两个:ObservableArrayMap
,ObservableArrayList
。
使用和普通的Map、List基本同样。直接看代码:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class=".Custom">
<variable
name="map"
type="android.databinding.ObservableArrayMap<String,String>" />
<variable
name="list"
type="android.databinding.ObservableArrayList<String>" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{map[`name`]}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{list[0]}"/>
</LinearLayout>
</layout>
在布局中,使用方式和普通的集合一样,假设看不太懂,能够往上翻博客,看上面的集合是怎么使用的。
在来看java文件,怎么设置数据,
ObservableArrayMap<String, String> map = new ObservableArrayMap<>();
ObservableArrayList<String> list = new ObservableArrayList<>();
map.put("name", "loader or qibin");
list.add("loader!!!");
binding.setMap(map);
binding.setList(list);
哦,太简单了。简直和List
、Map
用法一模一样!!!
好了,不多说了,大家也都看累了吧。 那就先讲到这里。其它的下篇博客我们继续学习。
demo源代码下载。戳这里
Android官方数据绑定框架DataBinding(一)的更多相关文章
- Android官方数据绑定框架DataBinding
数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那么多的findViewById. ...
- Android官方架构组件指南
此指南适用于那些曾经或现在进行Android应用的基础开发,并希望了解和学习编写Android程序的最佳实践和架构.通过学习来构建强大的生产级别的应用. 注意:此指南默认你对Android开发有比较深 ...
- 【腾讯Bugly干货分享】一步一步实现Android的MVP框架
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5799d7844bef22a823b3ad44 内容大纲: Android 开发 ...
- [Android]Android端ORM框架——RapidORM(v2.0)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5626716.html [Android]Android端ORM ...
- Android开源测试框架学习
近期因工作需要,分析了一些Android的测试框架,在这也分享下整理完的资料. Android测试大致分三大块: 代码层测试 用户操作模拟,功能测试 安装部署及稳定性测试 代码层测试 对于一般java ...
- Android官方技术文档翻译——Gradle 插件用户指南(5)
昨晚把第五章未译完的几句话攻克了.只是第六章没怎么译,明后天又是周末,假设周一前第六章翻译完的话,周一再发第六章. 本文译自Android官方技术文档<Gradle Plugin User Gu ...
- Android官方技术文档翻译——新构建系统概述
本文译自Android官方技术文档<New Build System>,原文地址:http://tools.android.com/tech-docs/new-build-system. ...
- Android官方技术文档翻译——开发工具的构建概述
本文译自Android官方技术文档<Build Overview>,原文地址:http://tools.android.com/build. 因为<Android Lint Chec ...
- Google官方网络框架Volley实战——QQ吉凶测试,南无阿弥陀佛!
Google官方网络框架Volley实战--QQ吉凶测试,南无阿弥陀佛! 这次我们用第三方的接口来做一个QQ吉凶的测试项目,代码依然是比较的简单 无图无真相 直接撸代码了,详细解释都已经写在注释里了 ...
随机推荐
- 【OC学习-8】存取器方法?getter和setter?事实上就是赋值和返回值的两种函数
我们在声明类的时候,有实例变量+方法(函数),这些实例变量假设默认的话都是protected类型,一般无法直接訪问.更别提赋值和调用了,所以就产生了两种函数,getter函数就是可以返回实例变量的值, ...
- absolute、relative,toggle()
測试代码例如以下: <div> <div class="global">不应用样式</div> <div class="glob ...
- java——简单理解线程
一·[概念] 一般来说,我们把正在计算机中运行的程序叫做"进程"(process),而不将其称为"程序"(program). 所谓"线程& ...
- js37---Function.prototype
//给函数的prototype新增名字为name,函数体为fn的函数 Function.prototype.method =function(name,fn){ this.prototype[name ...
- IK分词器插件elasticsearch-analysis-ik 6.1.1
http://88250.b3log.org/full-text-search-elasticsearch#b3_solo_h3_0 IK分词器插件 (1)源码 https://github.com/ ...
- C# 中 int、Convert.ToInt32()、int.Parse()的区别
int适合简单数据类型之间的转换,C#的默认整型是int32(不支持bool型); int.Parse(string sParameter)是个构造函数,参数类型只支持string类型; Conver ...
- LAN8720A网络模块的使用问题
一.LAN8720A模块驱动电路 最近在调试STM32F4驱动LAN8720A网络模块,在做方案前参考是正点原子的LAN8720A的驱动电路方案,但是从网上买回来的LAN8720A模块用正点原子的例程 ...
- Vijos——T1279 Leave-绿光
https://vijos.org/p/1279 背景 期待这一份幸运,和一份冲劲,多么奇妙的际遇…….燕姿在演唱完绿光这首歌后,出给了姿迷一个考题. 北欧有一个传说!人一生中能看见绿光!他就一生都可 ...
- 使用knockout.js 完毕template binding
//1.template <script id="txn-details-template" type="text/html"> <!--St ...
- python序列中是否包含某个元素
http://outofmemory.cn/code-snippet/9098/python-list-contains-with-in-not-in theList = ['a','b','c'] ...