适配器模式
一. 生活小场景
生活中我们将风能转换成我们的电能,如果说我们直接拿到风能,能够把我们的电灯发亮吗?是不行的,对不对,所以这是两个不能够兼容的东西,风能是不能够直接把我们的电灯泡点亮的,那怎么办?只能通过发电机将我们的风能转换成我们的电能,再通过电能去点亮电灯,所以我们的发电机就相当于适配器,它把两个不能兼容的接口,让他们兼容在了一起,宏观上来说也是风能点亮了我们的灯泡。
二. 适配器模式定义
适配器模式 (Adapter Pattern) 是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
三. 代码小案例
我们这里举例一个电脑通过usb链接显示器的hdmi的例子。
/**
* 目标
*/
public interface Target {
void method();
}
/**
* 具体目标是需要一个HDMI的接口
*/
public class ConcreteTarget implements Target{
@Override
public void method() {
Log.w("simple ConcreteTarget", "我需要使用HDMI接口");
}
}
/**
* 源数据,电脑和屏幕连接
*/
public class Adaptee {
public void method2() {
Log.w("simple Adatee", "我需要使用USB接口");
}
}
/**
* 桥梁连接,继承自源数据Adaptee,并且实现目标数据的接口,意思是两边都不落下,作为桥梁
*/
public class Adapter extends Adaptee implements Target{
@Override
public void method() {
//拿到源数据
super.method2();
//这句日志可以理解为转接头,源数据--->目标数据
Log.w("simple Adapter", "使用了USB转HDMI线,现在可以使用在HDMI线上了");
}
}
上面书写的这个适配器模式,可以理解为类层面的适配器模式,类适配器,灵活性不是很高。
我们修改下
/**
* 桥梁连接,继承自源数据Adaptee,并且实现目标数据的接口,意思是两边都不落下,作为桥梁
*/
public class Adapter extends Adaptee implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void method() {
//拿到源数据
adaptee.method2();
//这句日志可以理解为转接头,源数据--->目标数据
Log.w("simple Adapter", "使用了USB转HDMI线,现在可以使用在HDMI线上了");
}
}
测试代码:
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Adaptee adaptee = new Adaptee();
Adapter adapter = new Adapter(adaptee);
adapter.method();
}
}
从结果看出运行成功,意味着适配器桥接usb和hdmi两个接口成功了。
小结:
- 两个不兼容的内容进行了桥接整合,通过中介者Adapter进行连接。
- 希望有一个类转换成另外一个类,但是两个类不兼容,又希望他们能一起工作,那就比较适合使用我们的适配器模式了,提高类的复用。
四. RecyclerView的适配器模式
我们看下面这张图,我们的app的列表是会有很多的数据,数据都是存在集合上面的,但是我们的数据,也就是我们的集合是不能直接添加到我们的ViewGroup上面去的,为什么说是ViewGroup呢,因为列表嘛,肯定不会只有一个子view,数据是不能直接通过addView的方式直接添加到我们的视图上面去显示的。
所以我们需要做一个中转,需要一个适配器Adapter,它做什么工作,它把数据给改了,把集合中的数据改成一个个对应的View,然后再把这些View,通过addView就可以添加到我们的视图上面去了,
接下来我们写个Demo,感受一下适配器:
测试用的Activity
public class TestActivity extends AppCompatActivity {
private RecyclerView recyclerView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerview);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
//构建源数据,
List<String> data = new ArrayList<>();
data.add("111");
data.add("222");
data.add("333");
MyAdapter myAdapter = new MyAdapter(this, data);
recyclerView.setAdapter(myAdapter);
}
}
适配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context mContext;
private List<String> mData;
public MyAdapter(Context mContext, List<String> mData) {
this.mContext = mContext;
this.mData = mData;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_test, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
String data = this.mData.get(position);
holder.textView.setText(data);
}
@Override
public int getItemCount() {
return mData.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textview);
}
}
}
item子view的xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/textview"
android:textSize="30sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="111" />
</LinearLayout>
父布局的xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
最终的运行结果:
这就是一个经典的适配器模式的例子,也很常用,通过适配器将集合中的数据进行中转,再通过适配器addView。
五. 手写ListView体验适配器模式
我们手写自定义一个适配器,体验一下适配器模式的用途。
/**
* 手写简单的ListView,不考虑复用
*/
public class TestListView extends ScrollView {
private LinearLayout mContainer;
private ListAdapter mAdapter;
public TestListView(Context context) {
super(context, null);
}
public TestListView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mContainer = new LinearLayout(context);
mContainer.setOrientation(LinearLayout.VERTICAL);
super.addView(mContainer);
}
public TestListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContainer = new LinearLayout(context);
mContainer.setOrientation(LinearLayout.VERTICAL);
super.addView(mContainer);
}
public void addView(View child) {
mContainer.addView(child);
}
public void setAdapter(ListAdapter listAdapter) {
this.mAdapter = listAdapter;
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
View childView = mAdapter.getView(i, mContainer);
mContainer.addView(childView);
}
}
}
先自定义一个抽象类父类适配器,模拟ListView的两个方法,
public abstract class ListAdapter {
//获取多少条
public abstract int getCount();
//获取View
public abstract View getView(int position, ViewGroup viewGroup);
}
实现类
public class MyAdapter extends ListAdapter {
private Context mContext;
private List<String> mItems;
public MyAdapter(Context mContext, List<String> mItems) {
this.mContext = mContext;
this.mItems = mItems;
}
@Override
public int getCount() {
return mItems.size();
}
@Override
public View getView(int position, ViewGroup viewGroup) {
TextView itemView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.item_simple3, null);
itemView.setText(mItems.get(position));
return itemView;
}
}
测试类
public class TestActivity extends AppCompatActivity {
TestListView testListView;
List<String> mData;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testListView = findViewById(R.id.testView);
mData = new ArrayList<>();
for (int i = 0; i < 100; i++) {
mData.add(i + "");
}
MyAdapter myAdapter = new MyAdapter(this, mData);
testListView.setAdapter(myAdapter);
}
}
activity_main的xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.example.myapplication.TestListView
android:id="@+id/testView"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.example.myapplication.TestListView>
</LinearLayout>
itemView的xml布局
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TextView>
测试结果