Android冒险之旅-18-手机与平板适配的思路

1. 总体思路

  为主页面设计两个同名布局,分别为单页布局(手机),双页布局(平板)。单页布局存放在layout目录下,双页布局存放在layout-sw600dp目录下。运行时系统会根据具体设备选择合适的布局。在java代码中通过一个特殊的控件ID(比如双页有单页没有的)来判断当前显示的是单页模式还是双页模式。如果是双页模式,那么资源直接加载,如果是单页模式,那么某个点击操作触发之后跳转到另一页面。
  本文案例:郭霖大神第四章碎片的最佳实践,一个简易版新闻应用。

2. 主页面布局(单页)

layout/activity_news.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/news_title_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/news_title_fragment"
        android:name="qiyuan.lin.helloandroid.news.NewsTitleFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>

2. 主页面布局(双页)

layout-sw600dp/activity_news.xml

<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/news_title_fragment"
        android:name="qiyuan.lin.helloandroid.news.NewsTitleFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

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

        <fragment
            android:id="@+id/news_content_fragment"
            android:name="qiyuan.lin.helloandroid.news.NewsContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>

</LinearLayout>

4. 主活动

public class NewsActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news);
        /*
        * 这里开始解析主布局,而主布局静态绑定了Fragment碎片
        * 所以虽然没有任何代码
        * 但是下一步会加载碎片
        * 根据选择的主布局加载对应碎片
        * 单页布局加载NewsTitleFragment碎片
        * 双页模式加载NewsTitleFragment碎片和NewsContentFragment碎片
        * */
    }
}

5. NewsTitleFragment布局

news_title_frag.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycleview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

6. News实体类

/**
 * create by 星航指挥官
 * create on 2020/8/30
 * 我为天帝 当镇压世间一切敌
 * 遮天
 */
public class News {
    private String title;
    private String content;

    public News(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public News() {
    }

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

7. NewsTitleFragment

/**
 * create by 星航指挥官
 * create on 2020/8/30
 * 我为天帝 当镇压世间一切敌
 * 遮天
 */
public class NewsTitleFragment extends Fragment {
    @BindView(R.id.recycleview)
    RecyclerView recycleview;
    private boolean isTwoPane;

    @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);
        //使用ButterKnife快速绑定控件
        ButterKnife.bind(this,view);
        //初始化适配器
        setRecycleView();
        return view;
    }

    private void setRecycleView() {
        //线性布局:layoutManager
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        //设置recycle布局管理器
        recycleview.setLayoutManager(layoutManager);
        //创建Adapter实例
        NewsAdapter adapter = new NewsAdapter(getNews());
        //给RecycleView设置适配器
        recycleview.setAdapter(adapter);
    }
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        /*
        * 在主布局中查找news_content_layout
        * 这个ID对应的控件时新闻内容碎片的父控件
        * 如果这个控件存在
        * 那么证明NewsContentFragment也存在
        * 也就是加载的主布局为双页布局
        * */
        if (getActivity().findViewById(R.id.news_content_layout) != null) {
            isTwoPane = true;//双页模式
        } else {
            isTwoPane = false;//单页模式
        }
    }
    /*
    * 数据源
    * */
    private List<News> getNews() {
        List<News> newsList = new ArrayList<>();
        for (int i = 0; i <= 50; i++) {
            News news = new News();
            news.setTitle("This is Title " + i);
            news.setContent(getRandomLengthConten("This is Content " + i + ". "));
            newsList.add(news);
        }
        return newsList;
    }
    /*
    * 让NewsContent重复拼接
    * 模拟新闻内容
    * */
    private String getRandomLengthConten(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();
    }

    /*
    * 直接定义内部类 为recycleView创建适配器
    * */
    class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
        //存储传进来的数据源
        private List<News> mNewsList;
        //内部类ViewHolder用来保存布局控件
        class ViewHolder extends RecyclerView.ViewHolder {
            TextView newsTitleText;

            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                newsTitleText = itemView.findViewById(R.id.news_title);
            }
        }
        //重写适配器的构造器 传入数据源
        public NewsAdapter(List<News> mNewsList) {
            this.mNewsList = mNewsList;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            //解析子项布局
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
            //创建ViewHolder实例
            final ViewHolder holder = new ViewHolder(view);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //获取News实例
                    News news = mNewsList.get(holder.getAdapterPosition());
                    //根据加载的布局 选择对应的加载方式
                    if (isTwoPane) {
                        //如果是双页模式,则刷新NewsContentFragment中的内容
                        NewsContentFragment newsContentFragment = (NewsContentFragment)
                                getFragmentManager().findFragmentById(R.id.news_content_fragment);
                        //refresh()方法是newsContentFragment内提供的类方法,用于刷新内容
                        newsContentFragment.refresh(news.getTitle(), news.getContent());
                    } else {
                        //如果是单页布局 点击了标题之后会跳转到NewsContentActivity并显示news内容
                        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());
        }
        //返回recycleView的子项个数
        @Override
        public int getItemCount() {
            return mNewsList.size();
        }

    }

}

8. NewsContentFragment布局

news_content_frag.xml

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/news_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:textSize="20sp"/>
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/black"/>
        <TextView
            android:id="@+id/news_content"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            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="@color/black"/>

</RelativeLayout>

9. NewsContentFragment

/**
 * create by 星航指挥官
 * create on 2020/8/30
 * 我为天帝 当镇压世间一切敌
 * 遮天
 */
public class NewsContentFragment extends Fragment {
    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 visibilitylayout = view.findViewById(R.id.visibility_layout);
        visibilitylayout.setVisibility(View.VISIBLE);
        TextView newsTitleText = view.findViewById(R.id.news_title);
        TextView newscontent = view.findViewById(R.id.news_content);
        newsTitleText.setText(newsTitle);
        newscontent.setText(newsContent);
    }
}

10.NewsContentActivity 的布局

news_content

<?xml version="1.0" encoding="utf-8"?>
<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="vertical"
    tools:context=".news.NewsContentActivity">
    <fragment
        android:id="@+id/news_content_fragment"
        android:name="qiyuan.lin.helloandroid.news.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

11. NewsContentActivity

public class NewsContentActivity extends BaseActivity {

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

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

  建议创建空项目然后上面复制代码,跟着代码逻辑走一遍会更好的理解。要用到recycleView,别忘了添加依赖,冒险之旅14有专门介绍~
kee

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值