IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android初体验——探究碎片 -> 正文阅读

[移动开发]Android初体验——探究碎片

碎片概述

碎片是一种可以嵌入在活动中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上使用较广。一个屏幕中包含一个活动较空,可以通过构建两个碎片,包含不同的活动,在同一个活动中引入这两个碎片,可实现屏幕的充分利用

碎片的使用

碎片在平板中使用较广,因此后面的demo都使用了平板模拟器

碎片的简单使用

在一个活动中添加两个碎片,并让两个碎片平分活动空间
left_fragment.xml设置了一个按钮 并居中显示

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

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:layout_gravity="center_horizontal"
        android:text="Button"/>

</LinearLayout>

right_fragment.xml将该布局的背景设置为绿色,并放置一个TextView用于显示文本

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ABE6AB"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is right fragment"/>

</LinearLayout>

LeftFragment继承Fragment 重写了onCreateView()方法,在这个方法中通过LayoutInflater的inflater()方法将left_fragment布局动态加载进来

public class LeftFragment extends Fragment {

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

RightFragment

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

activity_main.xml使用<fragment>中添加碎片,通过android:name指明想要添加的碎片类名,将类的包名也加入。

<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"
    android:orientation="horizontal"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"/>

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"/>


</LinearLayout>

在这里插入图片描述

动态添加碎片

根据具体情况动态的添加碎片,可以将程序的界面制定的更多样化
another_right_fragment.xml

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is another right fragment"/>

</LinearLayout>

AnotherRightFragment

public class AnotherRightFragment extends Fragment {

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

activity_main.xml 将右侧碎片替换成FrameLayout,这是Android中最简单的布局,所有的控件都默认摆放在布局的左上角。由于这里仅需要在布局里放入一个碎片,不需要任何定位,因此使用该布局。

<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"
    android:orientation="horizontal"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"/>



    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/right_layout">

    </FrameLayout>


</LinearLayout>

MainActivity首先给左侧碎片中的按钮添加点击事件,然后调用replaceFragment()方法动态添加RightFragment这个碎片。当点击左侧按钮时调用replaceFragment()方法将右侧的碎片替换成AnotherRightFragment,这主要分为5步

  • 创建待添加的碎片实例
  • 获取FragmentManager,在活动中可以直接通过调用getSupportFragmentManager()方法得到
  • 开启一个事务,通过调用beginTransaction()方法开启
  • 向容器内添加或替换碎片,一般使用replace()实现,需要传入容器的id和待添加碎片的实例
  • 提交事务,调用commit()方法完成
public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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 = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.right_layout, fragment);
        transaction.commit();
    }
}

点击按钮后
在这里插入图片描述

在碎片中模拟返回栈

实现按一下Back返回上一个碎片 FragmentTransaction中提供了一个addToBackStack()方法,用于将一个事务添加到返回栈中,

//返回栈方法
        transaction.addToBackStack(null);

在提交事务之前调用该方法,它可以接收一个名字用于描述返回栈的状态,一般传入null。

碎片在活动之间进行通信

活动和碎片都是各自存在于一个独立的类当中,如何在活动中调用碎片里的方法或碎片中调用活动的方法,FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例。
活动中调用碎片中的方法

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

调用FragmentManager中的findViewById()方法 可以在活动中得到相应碎片的实例,这样就能轻松调用碎片里的方法了
碎片中调用活动中的方法

MainActivity activity = (MainActivity) getActivity();

当碎片中需要使用Context对象时,也可以使用getActivity()方法,因为获取到活动本身就是一个 Context对象。
碎片于碎片之间也可以进行通信,通过活动作为中间媒介。

碎片的生命周期

碎片的状态和回调

  • 运行状态
    当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。
  • 暂停状态
    当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入到暂停状态。
  • 停止状态
    当一个活动进入停止状态时,与它相关联的碎片就会进人到停止状态,或者通过调用FragmentTransaction的remove()replace()方 法将碎片从活动中移除,但如果在事务提交之前调用addToBackStack()方法,这时的碎片也会进人到停止状态。总的来说,进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收。
  • 销毁状态
    碎片总是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进人到销毁状态。或者通过调用FragmentTransaction 的remove()replace( )方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack()方法,这时的碎片也会进人到销毁状态。
    同样地,Fragment 类中也提供了一系列的回调方法,以覆盖碎片生命周期的每个环节。其中,活动中有的回调方法,碎片中几乎都有,不过碎片还提供了一些附加的回调方法
  • onAttach()
    当碎片和活动建立关联的时候调用。
  • onCreateView()
    为碎片创建视图(加载布局)时调用。
  • onActivityCreated()
    确保与碎片相关联的活动一定 已经创建完毕的时候调用。
  • onDestroyView()
    当与碎片关联的视图被移除的时候调用。
  • onDetach()
    当碎片和活动解除关联的时候调用。

在这里插入图片描述

体验碎片的生命周期

RightFragment在每个回调方法中都加入了打印日志代码

public class RightFragment extends Fragment {
    public static final String TAG = "RightFragment";

    //当碎片和活动建立关联时
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG, "onAttach");
    }

    //创建活动时
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
    }

    //为碎片创建布局时
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }

    //确保与碎片相关联的活动创建完毕时
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG, "onActivityCreated");
    }

    //当活动由可见变为不可见时
    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    //当活动准备好与用户进行交互时
    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    //当系统准备启动或恢复另一个活动时
    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

    //当活动变为完全不可见时
    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    //当与碎片关联的视图被移除时
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "onDestroyView");
    }

    //当活动被销毁前
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    //当碎片和活动解除关联时
    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG, "onDetach");
    }
}

运行程序 ,当RightFragment第一次被加载进屏幕时会依次执行onAttach(),onCreate(),onCreateView(),onActivityCreated(),onStart(),onResume()方法
在这里插入图片描述
点击按钮后,由于AnotherRightFragment替换了RightFragment, 此时的RightFragment进人了停止状态,因此onPause(). onStop()onDestroyView()方法会得到执行。当然如果在替换的时候没有调用addToBackStack()方法, 此时的RightFragment 就会进人销毁状态,onDestroy()onDetach()方法就会得到执行。
在这里插入图片描述
接着按下Back键,RightFragment 会重新回到屏幕。依次会执行onPause(). onStop(). onDest royView(). onDestroy()onDetach()方法,最终将活动和碎片一起销毁。
在这里插入图片描述
另外值得一提的是,在碎片中你也是可以通过onSaveInstanceState()方法来保存数据的,因为进入停止状态的碎片有可能在系统内存不足的时候被回收。保存下来的数据在onCreate().onCreateView( )onActivityCreated()这 3个方法中你都可以重新得到,它们都含有一个Bundle类型的savedInstanceState参数。

动态加载布局的技巧

使用限定符

很多的平板应用都采用的是双页模式(程序会在左侧的面板上显示一个包含子项的列表,在右侧的面板上显示内容),因为平板电脑的屏幕足够
大,完全可以同时显示下两页的内容,但手机的屏幕一次就只能显示一页的内容, 因此两个页面需要分开显示。
使用限定符可在运行时判断程序应该使用单页还是多页

activity_main.xml只留下了左侧碎片,并让它充满整个布局,只包含了一个碎片,即单页模式

<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"
    android:orientation="horizontal"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"/>
    
</LinearLayout>

接着在res目录下新建layout-large文件夹,在这个文件夹下新建一个布局,也叫作activity_ main.xml, 该布局包含了两个碎片,即双页模式。其中large就是一个限定符,那些被屏幕认为是large的设备就会自动加载layout-large文件夹下的布局,而小屏幕的设备则还是会加载layout文件夹下的布局。

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

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"/>

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"/>


</LinearLayout>

然后将MainActivity中replaceF ragment ()方法里的代码注释掉,并在平板模拟器上重新运行程序,
平板上
在这里插入图片描述
手机上在这里插入图片描述
这样我们就实现了在程序运行时动态加载布局的功能。
在这里插入图片描述
在这里插入图片描述

使用最小宽度限定符

最小宽度限定符允许我们对屏幕的宽度指定一个最小值 (以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局, 屏幕宽度小于这个值的设备就加载另一个布局。

在res目录下新建layout-sw600dp文件夹,然后在这个文件夹下新建activity_main.xml 布局,

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

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment"/>

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment"/>
    
</LinearLayout>

这就意味着,当程序运行在屏幕宽度大于600dp的设备上时,会加载layout-sw600dp/activity_ main布局,当程序运行在屏幕宽度小于600dp的设备上时,则仍然加载默认的layoutactivity_ main布局。

一个简易版的新闻应用

编写同时兼容手机和平板的应用程序
由于待会在编写新闻列表时会使用到RecyclerView,因此首先需要在app/build.gradle当中添加依赖库,如下所示:

implementation 'androidx.recyclerview:recyclerview:1.0.0'

新建一个新闻实体类,News

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类的代码还是比较简单的,title 字段表示新闻标题,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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/visibility_layout"
        android:orientation="vertical"
        android:visibility="invisible">
        
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/news_title"
            android:gravity="center"
            android:padding="10dp"
            android:textSize="20sp"/>
        
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000"/>
        
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:id="@+id/news_content"
            android:layout_weight="1"
            android:padding="15dp"
            android:textSize="18sp"/>
    </LinearLayout>
    
    <View
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:background="#000"/>

</RelativeLayout>

新闻内容的布局主要可以分为两个部分,头部部分显示新闻标题,正文部分显示新闻内容,中间使用一条细线分隔开。这里的细线是利用View来实现的,将View的宽或高设置为1dp,再通过background属性给细线设置一下 颜色就可以了。这里我们把细线设置成黑色。
在这里插入图片描述

android:padding

设置了内边距为10dp,padding为内边距;margin为外边距。

安卓的view是一块矩形区域,padding是内边距,就是view(里面的内容)永远都至少和边界有一段设定好的距离。margin是外边距,就是外面的view无法完全靠近这个view的边界,至少要间隔一段设置好的距离。

某个View指定为padding是针对该View里面的子View距离该View距离而言的。某个View指定为margin是针对该View本身距离别人或者父View而言的。

android:layout_alignParent

对于控件的 android:layout_alignParent 属性,只有在该布局的父布局也是RelativeLayout是才有用,此属性的含义为将控件边缘与父控件的边缘对齐

android:layout_alignParentLeft=“true” --将控件的左边缘和父控件的左边缘对齐
android:layout_alignParentTop=“true” --将控件的上边缘和父控件的上边缘对齐
android:layout_alignParentRight=“true” --将控件的右边缘和父控件的右边缘对齐
android:layout_alignParentBottom=“true” --将控件的底边缘和父控件的底边缘对齐

android:layout_centerInParent=“true” --将控件置于父控件的中心位置
android:layout_centerHorizontal=“true” --将控件置于水平方向的中心位置
android:layout_centerVertical=“true” --将控件置于垂直方向的中心位置

然后再新建一个NewsContentFragment类,继承自Fragment, 代码如下所示:

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;
    }
    
    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);
    }
}

首先在onCreateView( )方法里加载了我们刚刚创建的news_ content_ frag 布局,接下来又提供了一个refresh()方法,这个方法就是用于将新闻的标题和内容显示在界面上的。可以看到,这里通过findViewById( )方法分别获取到新闻标题和内容的控件,然后将方法传递进来的参数设置进去。
这样我们就把新闻内容的碎片和布局都创建好了,但是它们都是在双页模式中使用的,如果想在单页模式中使用的话,我们还需要再创建一个活动。右击com.example.fragmentbestpractice包→New→Activity→ Empty Activity, 新建一个NewsContentActity, 并将布局名指定成news_ content, 然后修改news_ content.xml 中的代码,如下所示:

<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=".NewsContentActivity"
    android:orientation="vertical">
    
    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/news_content_fragment"
        android:name="com.example.fragmentbestpractice.NewsContentFragment"/>

</LinearLayout>

这里我们充分发挥了代码的复用性,直接在布局中引入了NewsContentFragment, 这样也就相当于把news_ content. frag 布局的内容自动加了进来。
然后修改NewsContentActivity中的代码

public class NewsContentActivity extends AppCompatActivity {
    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)getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
        //刷新NewsContent-Fragment界面
        newsContentFragment.refresh(newsTitle, newsContent);
    }
}

可以看到,在onCreate( )方法中我们通过Intent 获取到了传人的新闻标题和新闻内容,然后调用FragmentManager的findFragmentById( )方法得到了NewsContentFragment的实例,接着调用它的refresh()方法,并将新闻的标题和内容传入,就可以把这些数据显示出来了。
接下来还需要再创建一个用于显示新闻列表的布局,新建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">

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

</LinearLayout>

这个布局的代码就非常简单了,里面只有一个用于显示新闻列表的RecyclerView。既然要用到RecyclerView,那么就必定少不了子项的布局。新建news_item.xml 作为RecyclerView子项的布局

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

子项的布局也非常简单,只有一个TextView。android: padding表示给控件的周围加上补白,这样不至于让文本内容会紧靠在边缘上。

android: singleLine

设置为true表示让这个TextView只能单行显示。

android:ellipsize

用于设定当文本内容超出控件宽度时,文本的缩略方式,这里指定成end表示在尾部进行缩略。

既然新闻列表和子项的布局都已经创建好了,那么接下来我们就需要一个用于展示新闻列表的地方。这里新建NewsTitleF ragment作为展示新闻列表的碎片

public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;


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

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

可以看到,NewsTitleFragment 中并没有多少代码,在onCreateView() 方法中加载了news_title_frag 布局,这个没什么好说的。我们注意看一下onActivityCreated()方法,这个方法通过在活动中能否找到一个id 为news_ content_ layout 的View来判断当前是双页模式还是单页模式,因此我们需要让这个id为news_ content_layout 的View只在双页模式中才会出现。
activity_main.xml

<FrameLayout 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:id="@+id/news_title_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/news_title_fragment"
        android:name="com.example.fragmentbestpractice.NewsTitleFragment"/>

</FrameLayout>

上述代码表示,在单页模式下,只会加载一个新闻标题的碎片。
然后新建layout-sw600dp文件夹,在这个文件夹下再新建一一个activity_ main.xml 文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:name="com.example.fragmentbestpractice.NewsTitleFragment"
        android:id="@+id/news_title_fragment"/>
    
    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:id="@+id/news_content_layout">
        
        <fragment
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/news_content_fragment"
            android:name="com.example.fragmentbestpractice.NewsContentFragment"/>
    </FrameLayout>

</LinearLayout>

可以看出,在双页模式下我们同时引入了两个碎片,并将新闻内容的碎片放在了一个Frame-Layout布局下,而这个布局的id正是news_ content layout。 因此,能够找到这个id的时候就是双页模式,否则就是单面模式。
我们在NewsTitleFragment中新建一个内部类NewsAdapter来作为RecyclerView的适配器

public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;


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

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

    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 view) {
                    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();
        }
    }
}

之前我们都是将适配器写成一个独立的类,其实也是可以写成内部类的,这里写成内部类的好处就是可以直接访问NewsTitleF ragment的变量,比如isTwoPane。
观察一下onCreateViewHolder( )方法中注册的点击事件,首先获取到了点击项的News实例,然后通过isTwoPane变量来判断当前是单页还是双页模式,如果是单页模式,就启动一个新的活动去显示新闻内容,如果是双页模式,就更新新闻内容碎片里的数据。现在还剩最后一步收尾工作,就是向RecyclerView 中填充数据了。修改NewsTitle-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;
    }
    
    private List<News> getNews() {
        List<News> newsList = new ArrayList<>();
        for(int i = 1; i <= 50; i++) {
            News news = new News();
            news.setTitle("This is new title" + i);
            news.setContent(getRandomLengthContent("This is news content" + i + "."));
            newsList.add(news);
        }
        return newsList;
    }
    
    private String getRandomLengthContent(String contnet) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < length; i++) {
            builder.append(contnet);
        }
        return builder.toString();
    }

可以看到,onCreateView( )方法中添加了RecyclerView标准的使用方法。另外,这里调用了getNews ()方法来初始化50条模拟新闻数据,同样使用了一个getRandomLengthContent()方法来随机生成新闻内容的长度,以保证每条新闻的内容差距比较大
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-31 16:45:30  更:2021-07-31 16:48:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 3:47:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码