第一行代码之碎片(fragment)

标签: Android

4.2 碎片的使用方式

4.2.1 碎片的简单用法

新建一个左侧碎片布局 fragment_left.xml,代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="按钮"/>

</LinearLayout>

然后新建右侧碎片布局fragment_right.xml,代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#00ff00">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="这是右边的fragment"/>

</LinearLayout>

将布局的背景色设置成绿色,并放置了一个 TextView 用于显示一段文本

 

建议使用 support-v4 库中的 Fragment,因为它可以让碎片在所有 Android 系统版本中保持功能一致性

LeftFragment 的代码如下所示:

public class LeftFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_left, container, false);
        return view;
    }
}

这里仅仅是重写了 Fragment 的 onCreateView()方法

然后在这个方法中通过 LayoutInflater 的 inflate()方法将刚才定义的 fragment_left 布局动态加载进来

接着我们用同样的方法再新建一个 RightFragment:

public class RightFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                          Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_right, container, false);
        return view;
    }
}

接下来修改 activity_fragment.xml 中的代码,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <fragment
        android:id="@+id/fragment_left"
        android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <fragment
        android:id="@+id/fragment_right"
        android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
    
</LinearLayout>

可以看到,我们使用了<fragment>标签在布局中添加碎片

通过 android:name 属性来显式指明要添加的碎片类名,注意一 定要将类的包名也加上

这样最简单的碎片示例就已经写好了,运行一下程序,(平板上)效果如图:

4.2.2 动态添加碎片

碎片真正的强大之处在于,它可以在程序运行时动态地添加到活动当中

根据具体情况来动态地添加碎片,你就 可以将程序界面定制得更加多样化

在上一节代码的基础上继续完善,新建 fragment_another_right.xml,代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffff00">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="这是另外一个右边的fragment"/>

</LinearLayout>

然后新建 AnotherRightFragment 作为另一个右侧碎片,代码如下所示:

public class AnotherRightFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_another_right, container, false);
        return view;
    }
}

接下来看一下如何将它动态地添加到活动当中。修改 activity_fragment.xml,代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <fragment
        android:id="@+id/fragment_left"
        android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <FrameLayout
        android:id="@+id/right_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
    
    <!--
    <fragment
        android:id="@+id/fragment_right"
        android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
         -->

</LinearLayout>

可以看到,现在将右侧碎片放在了一个 FrameLayout 中

下面在代码中向 FrameLayout 里添加内容,从而实现动态添加碎片的功能

修改 Activity 中的代码,如下所示:

public class FragmentActivity extends AppCompatActivity implements View.OnClickListener {

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

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        replaceFragment(new RightFragment());
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button:
                replaceFragment(new AnotherRightFragment());
                break;

            default:
                break;
        }
    }

    private void replaceFragment(Fragment fragment){
        // 获取FragmentManager
        FragmentManager fragmentManager = getSupportFragmentManager();
        // 开启事务
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 添加或替换碎片
        transaction.replace(R.id.right_layout,fragment);
        // 提交事务
        transaction.commit();
    }
}

上述代码,给左侧碎片中的按钮注册了一个点击事件

调用 replaceFragment() 方法动态添加碎片

结合代码可看出,动态添加碎片主要分为 5 步

 

1. 创建待添加的碎片实例

2. 获取 FragmentManager,在活动中可以直接调用 getSupportFragmentManager()方法得到

3. 开启一个事务,通过调用 beginTransaction()方法开启

4. 向容器内添加或替换碎片,使用 replace() 方法实现,需要传入容器的 id 和待添加的碎片实例。

5. 提交事务,调用 commit()方法来完成。

重新运行程序,效果如图:

4.2.3 在碎片中模拟返回栈

点击按钮添加了一个碎片之后,按下 Back 键程序就会直接退出

如果这里我们想模仿类似于返回栈的效果,按下 Back 键可以回到上一个碎片,该如何实现呢?

FragmentTransaction 中提供了一个 addToBackStack() 方法

可以用于将一个事务添加到返回栈中,修改 Activity 中的代码,如下所示:

public class FragmentActivity extends AppCompatActivity implements View.OnClickListener {

    . . .

    private void replaceFragment(Fragment fragment){
        // 获取FragmentManager
        FragmentManager fragmentManager = getSupportFragmentManager();
        // 开启事务
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 添加或替换碎片
        transaction.replace(R.id.right_layout,fragment);
        // 用于描述返回栈的状态
        transaction.addToBackStack(null);
        // 提交事务
        transaction.commit();
    }
}

在事务提交之前调用了 FragmentTransaction 的 addToBackStack() 方法

它可以接收一个名字用于描述返回栈的状态,一般传入 null 即可

4.2.4 碎片和活动之间进行通信

为了方便碎片和活动之间进行通信,FragmentManager 提供了一个类似于 findViewById() 的方法

专门用于从布局文件中获取碎片的实例。在活动中调用碎片里的方法:

RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);

在碎片中调用活动里的方法:通过调用 getActivity()方法来得到和当前碎片相关联的活动实例

代码如下所示:

MainActivity activity = (MainActivity) getActivity();

4.3 碎片的生命周期

和活动 Acitvity 相似,Fragment 类中也提供了一系列的回调方法,以覆盖碎片生命周期的每个环节。其中,活动中有的回调方法,碎片中几乎都有,不过碎片还提供了一些附加的回调方法,重点来看下这几个回调:

  • onAttach() 当碎片和活动建立关联的时候调用。
  • onCreateView() 为碎片创建视图(加载布局)时调用。
  • onActivityCreated() 确保与碎片相关联的活动一定已经创建完毕的时候调用。
  • onDestroyView() 当与碎片关联的视图被移除的时候调用。
  • onDetach() 当碎片和活动解除关联的时候调用。

另外,在碎片中你也可以通过onSaveInstanceState()方法来保存数据

因为进入停止状态的碎片有可能在系统内存不足的时候被回收

保存下来的数据在 onCreate()、onCreateView() 和 onActivityCreated()这三个方法中

你都可以重新得到,它们都含有一个 Bundle 类型的 savedInstanceState 参数

4.5 碎片的最佳实践——一个简易版的新闻应用

添加后面需要用到的 RecyclerView 依赖库

接下来,准备好一个新闻的实体类,新建类 News,代码如下所示:

/**
 * 新闻实体类
 * Created by KXwon on 2016/12/12.
 */

public class News {

    private String title;   // 新闻标题

    private String content; // 新闻内容

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

接着新建一个 news_content_frag.xml 布局,作为新闻内容的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/visibility_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="invisible">

        <TextView
            android:id="@+id/news_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="20sp"
            android:padding="10dp"/>
        
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000"/>

        <TextView
            android:id="@+id/news_content"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:textSize="18sp"
            android:padding="15dp"/>
        
    </LinearLayout>

    <View
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:background="#000"/>

</RelativeLayout>

新闻内容的布局主要分为两个部分

头部显示新闻标题,正文显示新闻内容

中间使用一条细线分隔开

 

然后再新建一个 NewsContentFragment 类,如下:

/**
 * 新闻内容fragment
 * Created by KXwon on 2016/12/12.
 */

public class NewsContentFragment extends Fragment {

    private View view;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.news_content_frag, container, false);
        return view;
    }

    /**
     * 将新闻标题和新闻内容显示在界面上
     * @param newsTitle   标题
     * @param newsContent 内容
     */
    public void refresh(String newsTitle, String newsContent) {
        View visibilityLayout = view.findViewById(R.id.visibility_layout);
        visibilityLayout.setVisibility(View.VISIBLE);
        TextView newsTitleText = (TextView) view.findViewById (R.id.news_title);
        TextView newsContentText = (TextView) view.findViewById(R.id.news_content);
        newsTitleText.setText(newsTitle); // 刷新新闻的标题
        newsContentText.setText(newsContent); // 刷新新闻的内容
    }
}

这样就把新闻内容的碎片和布局创建好了

但它们都是在双页模式下使用的,若要在单页模式中使用

还需创建一个活动 NewsContentActivity,其布局 news_content.xml 中的代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/news_content_fragment"
        android:name="com.wonderful.myfirstcode.chapter4.simple_news.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

这里直接在布局中引入了 NewsContentFragment

相当于把 news_content_frag 布局的内容自动加了进来
然后编写 NewsContentActivity 的代码,如下:

public class NewsContentActivity extends AppCompatActivity {

    /**
     * 构建Intent,传递所需数据
     * @param context
     * @param newsTitle
     * @param newsContent
     */
    public static void actionStart(Context context, String newsTitle, String newsContent) {
        Intent intent = new Intent(context, NewsContentActivity.class);
        intent.putExtra("news_title", newsTitle);
        intent.putExtra("news_content", newsContent);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_content);
        // 获取传入的新闻标题、新闻内容
        String newsTitle = getIntent().getStringExtra("news_title");
        String newsContent = getIntent().getStringExtra("news_content");
        // 获取 NewsContentFragment 实例
        NewsContentFragment newsContentFragment = (NewsContentFragment) getSupportFragmentManager()
                .findFragmentById(R.id.news_content_fragment);
        // 刷新 NewsContentFragment 界面
        newsContentFragment.refresh(newsTitle, newsContent); 
    }
}

在 onCreate() 方法中通过 Intent 获取传入的新闻标题和内容

然后调用 FragmentManager 的 findFragmentById() 方法得到 NewsContentFragment 的实例

接着调用它的 refresh() 方法,并将新闻的标题和内容传入,显示数据

(关于 actionStart() 方法可以阅读前面的探究活动2.5.2相关笔记。)

接下来还需再创建显示新闻列表的布局 news_title_frag.xml,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/news_title_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

新建 news_item.xml 作为 上述 RecyclerView 子项的布局:

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/news_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="true"
    android:ellipsize="end"
    android:textSize="18sp"
    android:padding="10dp"/>

子项的布局就只有一个 TextView

新闻列表和子项布局都创建好了,接下来就需要一个用于展示新闻列表的地方

这里新建 NewsTitleFragment 作为展示新闻列表的碎片:

/**
 * 新闻列表fragment
 * Created by KXwon on 2016/12/12.
 */

public class NewsTitleFragment extends Fragment{

    private boolean isTowPane;

    @Override
    public View onCreateView(LayoutInflater inflater,  ViewGroup container,  Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.news_content_frag, container, false);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (getActivity().findViewById(R.id.news_content_layout)!= null){
            // 可以找到 news_content_layout 布局时,为双页模式
            isTowPane = true;
        }else {
            // 找不到 news_content_layout 布局时,为单页模式
            isTowPane = false;
        }
    }
}

为实现上述 onActivityCreated() 方法中判断当前时双页还是单页模式

接下来在 NewsTitleFragemt 中新建一个内部类 NewsAdapter 来作为 RecyclerView 的适配器

如下:

public class NewsTitleFragment extends Fragment{

    private boolean isTowPane;

    . . .
    
    class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

        private List<News> mNewsList;

        class ViewHolder extends RecyclerView.ViewHolder {

            TextView newsTitleText;

            public ViewHolder(View view) {
                super(view);
                newsTitleText = (TextView) view.findViewById(R.id.news_title);
            }
        }

        public NewsAdapter(List<News> newsList) {
            mNewsList = newsList;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
            final ViewHolder holder = new ViewHolder(view);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    News news = mNewsList.get(holder.getAdapterPosition());
                    if (isTwoPane) {
                        // 若是双页模式,则刷新 NewsContentFragment 中的内容
                        NewsContentFragment newsContentFragment = (NewsContentFragment)
                                getFragmentManager().findFragmentById(R.id.news_content_fragment);
                        newsContentFragment.refresh(news.getTitle(), news.getContent());
                    } else {
                        // 若是单页模式,则直接启动 NewsContentActivity
                        NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
                    }
                }
            });
            return holder;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            News news = mNewsList.get(position);
            holder.newsTitleText.setText(news.getTitle());
        }

        @Override
        public int getItemCount() {
            return mNewsList.size();
        }

    }

需要注意的是,这里把适配器写成内部类是为了直接访问 NewsTitleFragment 的变量

 比如:isTowPane

现在还剩最后一步收尾工作,就是向 RecyclerView 中填充数据了

修改 NewsTitleFragment 中的代码,如下所示:

public class NewsTitleFragment extends Fragment{

    . . .

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.news_title_frag, container, false);
        
        RecyclerView newsTitleRecyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        newsTitleRecyclerView.setLayoutManager(layoutManager);
        NewsAdapter adapter = new NewsAdapter(getNews());
        newsTitleRecyclerView.setAdapter(adapter);
        
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (getActivity().findViewById(R.id.news_content_layout) != null) {
            // 可以找到news_content_layout布局时,为双页模式
            isTwoPane = true;
        } else {
            // 找不到news_content_layout布局时,为单页模式
            isTwoPane = false;
        }
    }

    /**
     * 初始化50条模拟新闻数据
     * @return
     */
    private List<News> getNews() {
        List<News> newsList = new ArrayList<>();
        for (int i = 1; i <= 50; i++) {
            News news = new News();
            news.setTitle("This is news title " + i);
            news.setContent(getRandomLengthContent("新闻内容吼吼吼" + i + "!"));
            newsList.add(news);
        }
        return newsList;
    }

    /**
     * 随机生成不同长度的新闻内容
     * @param content
     * @return
     */
    private String getRandomLengthContent(String content) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            builder.append(content);
        }
        return builder.toString();
    }

    . . . 
}

到这里,所有的代码编写工作就完成了,运行程序,效果如下:

点击一条新闻,会启动一个新的活动来显示新闻内容:

版权声明:本文为netbar4原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/netbar4/article/details/82776901

智能推荐

php输出语句

php输出语句 常见的输出语句 echo(): 可以一次输出多个值,多个值之间用逗号分隔。echo是语言结构(language construct),而并不是真正的函数,因此不能作为表达式的一部分使用。 print(): 函数print()打印一个值(它的参数),如果字符串成功显示则返回true,否则返回false。 print_r(): 可以把字符串和数字简单地打印出来,而数组则以括起来的键和值...

工厂模式

简介 常见的实例化对象模式。 用工厂方法替代new操作的一种模式。 当我们使用new操作实例化对象时,调用构造函数完成初始化。若初始化仅是进行赋值等简单的操作,写入构造函数即可。但如果初始化时需要执行一长串复杂的代码,将多个工作装入一个方法,是不妥的。 创建实例与使用实例分离。将创建实例所需的大量初始化工作从基类的构造函数中分离出去。 简单工厂模式、工厂方法模式针对的是一个产品等级结构;而抽象工厂...

B1105 Spiral Matrix (画图)

B1105 Spiral Matrix (25分) //第一次只拿了21分 矩阵的长和宽,求最大因子,从sqrt(num)开始枚举. 每次循环一次,s++,t--,d--,r++ 测试点四运行超时,是因为输入一个数字的时候,需要直接输出这个数字。//1分 测试点二运行超时,最后一个数字不必再while循环一次,直接输出即可。//3分 最后一个测试点卡了好久/(ㄒoㄒ)/~~ 螺旋矩阵...

Java基础=>String,StringBuffer与StringBuilder的区别

字符串常量池 什么是字符串常量池? JVM为了减少字符串对象的重复创建,其维护了一块特殊的内存,这段内存被称为字符串常量池(存储在方法区中)。 具体实现 当代码中出现字符串时,JVM首先会对其进行检查。 如果字符串常量池中存在相同内容的字符串对象,如果有,则不再创建,直接返回这个对象的地址返回。 如果字符串常量池中不存在相同内容的字符串对象,则创建一个新的字符串对象并放入常量池,并返回新创建的字符...

java调用其他java项目的Https接口

项目中是这样的: 用户拿出二维码展示,让机器识别二维码, 机器调用开门的后台系统接口, 然后开门的后台系统接口需要调用管理系统的接口, 管理系统需要判断能不能开门.这两个系统是互相独立的.当时使用http调用是没有问题的.当时后来要求必须用https.废话不说,直接代码: 我的项目中调用的是 HttpsUtils.Get(utlStr) 这个接口 开门系统接口如下图:   管理系统的接口...

猜你喜欢

Hadoop1.2.1全分布式模式配置

一 集群规划 主机名            IP                               安装的软件 &nbs...

Go语言gin框架的安装

尝试安装了一下gin,把遇到的一些小问题来记录一下 安装步骤 首先来看看官方文档,链接点这里 可以看到安装步骤很简单,就一句话 在命令行中输入这句话运行等待就好。 问题来了,因为墙的问题,go get会很慢,所以命令行里面半天什么反应也没有,不要急,慢慢等着就会看到gin-gonic/gin这个目录出现 这个时候命令行还是没有结束,表示还在下一些东西。有的时候可能心急的人就停了(比如我),然后写个...

uni-app表单组件二

input(输入框) 属性名 类型 说明 平台差异 value String 输入框的初始内容 type String input 的类型 password Boolean(默认false) 是否是密码类型 placeholder String 输入框为空时占位符 placeholder-style String 指定 placeholder 的样式 placeholder-class Strin...

深入理解 JavaScript 代码执行机制

深入理解 JavaScript 代码执行机制 前言 本文仅为个人见解,如有错误的地方欢迎留言区探讨和指正。 1、餐前甜品 如下是一段 JavaScript 代码,如果你毫不犹豫的说出代码执行顺序。那么请直接滚动到底部,留下你的足迹,接受膜拜。如果还不是很确定,那么请往下继续查看。 2、磨刀不误砍柴工(了解浏览器原理) (1) 进程和线程 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小...

Centos7下配置DRBD Cluster扩展节点

操作环境 CentOS Linux release 7.4.1708 (Core) DRBDADM_BUILDTAG=GIT-hash:\ ee126652638328b55dc6bff47d07d6161ab768db\ build\ by\ [email protected]\,\ 2018-07-30\ 22:23:07 DRBDADM_API_VERSION=2 DRBD_KERNEL_VER...