• Android--碎片


    为了解决平板和手机之间的视觉效果而产生,比如一些界面在手机上看起来非常美观,但在平板电脑上看上去就可能会有控件被过分拉长、元素之间空隙过大等情况。

    一、 碎片是什么?

    碎片(Fragment)是一种可以嵌入在活动当中的UI片段,他能让程序更加合理和充分地利用大屏幕空间,因而在平板上应用得非常广泛。
    在手机中排放一个新闻列表,点击一个标题时,就打开另一个界面显示新闻详情信息。
    在这里插入图片描述

    在平板中,则使用两个碎片,将新闻列表界面和新闻详情页面分别放在两个碎片中,屏幕也就可以被充分利用起来了。
    在这里插入图片描述

    二、 碎片的简单介绍

    首先创建一个平板模拟器
    在这里插入图片描述

    1. 碎片的简单用法

    (1)新建左侧布局left_fragment.xml,右侧同样
    在这里插入图片描述

    (2)接着新建一个LeftFragment类,右侧同样,将button换成TextView

        

    (3)修改activity_main.xml中的代码

        
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (4)结果如下所示
    在这里插入图片描述

    2. 动态添加碎片

    上面已经学会了在布局文件中添加碎片的方法,我们可以在程序运行时动态地添加到活动中。根据具体情况来动态地添加碎片,可以将程序界面定制的更加多样化。
    (1) 在上面的基础上,首先新建一个another_right_fragment.xml,并只是将上面的right_fragment.xml改变颜色
    android:background=“#EED5B7”

    (2) 新建一个AnthorRightFragment类,代码如下:
    public class AnthorRightFragment extends Fragment {
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view =inflater.inflate(R.layout.another_right_fragment,container,false);
        return view;
    }
    
    • 1
    • 2
    • 3

    }

    (3) 在活动.xml主页面中修改写文档部分,代码如下:

    (4) 在FragmentLayout中添加内容,从而实现动态添加碎片的功能。修改MainActivity代码。
    动态添加碎片分为五步:
    1:创建待添加的碎片实例
    2:获取 FragmentManager,并调用getSupportFragmentManager()
    3:开启一个事务,并调用beginTransaction()开启
    4:向容器添加或替换碎片,调用.replace()方法实现
    5: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 v) {
        switch (v.getId()){
            case R.id.button:
                replaceFragment(new AnthorRightFragment());
                break;
            default:
                break;
        }
    }
    
    private void replaceFragment(Fragment fragment){
        FragmentManager fragmentManager =getSupportFragmentManager();
        FragmentTransaction transaction =fragmentManager.beginTransaction();
        transaction.replace(R.id.right_layout,fragment);//向容器内添加或替换碎片
        transaction.commit();//提交事务
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    }

    (5)结果如图所示,点击Button后,会变为右图所示
    在这里插入图片描述

    3. 在碎片中模拟返回栈

    按back键回到上一个碎片,修改MainActivity,添加一个 transaction.addToBackStack(null);
    private void replaceFragment(Fragment fragment){
    FragmentManager fragmentManager =getSupportFragmentManager();
    FragmentTransaction transaction =fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout,fragment);//向容器内添加或替换碎片
    transaction.addToBackStack(null);
    transaction.commit();//提交事务
    }

    4. 碎片和活动之间进行通信

    在活动中得到相应的碎片实例:调用FragmentManager的findFragmentById();方法
    碎片中调用活动里的方法:MainActivity activity =(MainActivity)getActivity();

    三、 碎片的生命周期

    1. 碎片的状态和回调

    (1) 运行状态
    (2) 暂停状态
    (3) 停止状态:进入停止状态的碎片对用户是完全不可见的,有可能被系统回收
    (4) 销毁状态
    在这里插入图片描述

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

    2. 体验碎片的生命周期

    修改RightFragment中的代码,如图所示:
    public static final String TAG = “RightFragment”;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        Log.d(TAG, "onAttach: 11");
    }
    
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
    }
    @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: ");
    }
    
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: ");
        View view =inflater.inflate(R.layout.right_fragment,container,false);
        return view;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    运行程序后,在打印日志的代码中能看到打印信息。
    在这里插入图片描述

    点击button按钮后,RightFragment就会进入销毁状态。
    在这里插入图片描述

    点击back键后,重新回到了运行状态。
    在这里插入图片描述

    再次按下Back退出程序,打印的信息如图所示。
    在这里插入图片描述

    四、 动态加载布局的技巧

    1. 使用限定符(Qualifiers)

    平板显示两页内容,但手机只能显示一页内容,如何判断使用双页模式还是单页模式呢?
    (1) 首先在activity_main.xml中,修改left页面的代码,让他充满整个父布局。
    android:layout_width=“match_parent”

    (2) 新建layout-large文件夹,并新建activity_main.xml。代码如下。
    (3) 其中large就是一个限定符,那些屏幕被认为是large的设备就会自动加载layout-large文件夹下的布局,而屏幕小的设备则会加载layout下的布局。



    在这里插入图片描述
    Android常见限定符
    在这里插入图片描述

    2. 使用最小宽度限定符

    large限定符解决了单双页的问题,那么large到底指多大呢?
    可以在res目录下新建layout-sw600dp文件夹,并新建activity_main.xml布局。
    这就是说,当程序运行屏幕宽度大于600dp时候,会自动加载这个布局。

    五、 实践–简易版的新闻应用

    (1) 首先在app/build.gradle中添加依赖库
    implementation ‘androidx.recyclerview:recyclerview:1.2.1’

    (2) 新建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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    }

    (3) 新建布局文件news_content_frag.xml,用于作为新闻内容的布局:
    布局主要分为两部分,头部部分显示新闻标题,正文部分显示新闻内容,中间再使用一条细线(View)分隔开。

        
    
            
            
            
    
        
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (4) 新建NewsContentFragment类,继承自Fragment。
    在onCreateView()方法,加载了news_content_frag布局。 refresh()方法是用于将新闻的标题和内容显示在界面上的。
    public class NewsContentFragment extends Fragment {
    private View view;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    view = inflater.inflate(R.layout.news_content_frag, container, false);
    return view;
    }
    public void refresh(String newsTitle, String newsContent) {
    View visibiltyLayout = view.findViewById(R.id.visibility_layout);
    visibiltyLayout.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);//刷新新闻内容
    }
    }

    (5) 上面所示,都是在双页模式中使用的,如果想在单页模式上使用的话,需要再创建一个活动,NewsContentActivity类,并将布局名称指定成news_content.xml.


    (6) 在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);
        newsContentFragment.refresh(newsTitle,newsContent);//刷新newsContentFragment界面
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    }

    (7) 创建新闻列表布局,新建news_title_frag.xml,里面只有一个用于显示新闻列表的RecyclerView。

    android:layout_width=“match_parent”
    android:layout_height=“match_parent”
    android:id=“@+id/news_title_recycler_view”/>

    (8) 新建news_item.xml作为recyclerView子项布局

    (9) 新建NewsTitleFragment类,作为展示新闻列表的地方。
    package com.example.fragmentbestpractice;

    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;

    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.recyclerview.widget.LinearLayoutManager;
    import androidx.recyclerview.widget.RecyclerView;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;

    public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;
    
    class NewsAdapter extends RecyclerView.Adapter {
        private List mNewsList;
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull 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) getFragmentManager().findFragmentById(R.id.news_content_fragment);
                        newsContentFragment.refresh(news.getTitle(), news.getContent());
    
                    } else {
                        NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
                    }
                }
            });
    
            return holder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            News news = mNewsList.get(position);
            holder.newsTitleText.setText(news.getTitle());
        }
    
        @Override
        public int getItemCount() {
            return mNewsList.size();
        }
    
        class ViewHolder extends RecyclerView.ViewHolder {
            TextView newsTitleText;
    
            public ViewHolder(@NonNull View view) {
                super(view);
                newsTitleText = (TextView) view.findViewById(R.id.news_title);
            }
        }
    
        public NewsAdapter(List newsList) {
            mNewsList = newsList;
        }
    }
    
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable 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 getNews() {
        List newsList = new ArrayList<>();
        for (int i = 1; i <= 50; i++) {
            News news = new News();
            news.setTitle("title~~~~~~~" + i);
            news.setContent(getRandomLengthContent("this is news content" + i + "."));
            newsList.add(news);
        }
        return newsList;
    }
    
    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();
    }
    
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (getActivity().findViewById(R.id.news_content_layout) != null) {
            isTwoPane = true;
        } else {
            isTwoPane = false;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    }

    (10) 修改activity_main.xml中的代码

        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (11) 在layout-sw600dp文件夹中,新建一个activity_main.xml文件。

        
        
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (12) 在NewsTitleFragment中新增适配器。
    下图为手机新闻
    在这里插入图片描述

    下图为平板新闻

    在这里插入图片描述

  • 相关阅读:
    关于nacos的配置获取失败及服务发现问题的排坑记录
    【分布式】Rabbitmq死信队列模型、实战场景---订单延迟30min支付处理
    基于STM32的USART、UART串口命令调制和解析(加密与解密)
    ansible介绍、主机清单、临时命令的使用
    Python教程(13)——Python运算符详解。算术运算符|比较运算符|逻辑运算符|位运算符
    从“新零售”到“即时零售”看中国电商之变
    Clickhouse学习笔记
    一面高频vue面试题
    数组:4.覆盖的点数
    Codeforces Round #826 (Div. 3) D E F
  • 原文地址:https://blog.csdn.net/qq_48019875/article/details/126091412