说说 Android 中如何实现同时兼容手机与平板的新闻应用界面

企业一般都会为应用提供手机版与 Pad 版的程序,我们可以利用 Android 碎片,编写出兼容手机与平板的应用程序。

手机模拟器运行效果

因为新闻列表会用到 RecyclerView,所以我们先在 app/build.gradle 中引入依赖库:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support:recyclerview-v7:24.2.1'
    testCompile 'junit:junit:4.12'
}

接下来,编写一个新闻的实体类:

public class News {

    /**
     * 标题
     */
    private String title;

    /**
     * 内容
     */
    private String content;

    /**
     * 图片列表
     */
    private List<Integer> images;

    public News(String title, String content) {
        this.title = title;
        this.content = 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;
    }

    public List<Integer> getImages() {
        return images;
    }

    public void setImages(List<Integer> images) {
        this.images = images;
    }

    @Override
    public String toString() {
        return "News{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", images=" + images +
                '}';
    }
}

新闻的实体类包含标题、内容和图片资源列表。

新建布局 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: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="left"
            android:padding="10dp"
            android:textSize="26sp"
            android:textColor="#000"
            android:lineSpacingExtra="3dp"
            />

        <!--分割线-->
        <View android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#d0d0d0"/>

        <ImageView
            android:id="@+id/news_image"
            android:layout_width="400sp"
            android:layout_height="300sp"
            android:layout_gravity="center"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            />

        <!--内容-->
        <TextView
            android:id="@+id/news_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingRight="15dp"
            android:textSize="18sp"
            android:lineSpacingExtra="3dp"
            />
    </LinearLayout>
</RelativeLayout>

新闻内容的布局主要分为两个部分,头部显示新闻标题,正文显示新闻图片与内容,中间使用一条细线分隔开。细线采用 View 实现,把 View的宽与高都设置为 1dp,然后再通过 background 设置背景色即可。

然后再新建一个 NewsContentFragment 类,作为新闻内容的碎片:

public class NewsContentFragment extends Fragment {

    private View view;

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

    /**
     * 更新
     *
     * @param title      标题
     * @param content    内容
     * @param imageResId 图片资源 ID
     */
    public void refresh(String title, String content, int imageResId) {
        view.findViewById(R.id.visibility_layout).setVisibility(View.VISIBLE);
        ((TextView) view.findViewById(R.id.news_title)).setText(title);
        ((ImageView) view.findViewById(R.id.news_image)).setImageResource(imageResId);
        ((TextView) view.findViewById(R.id.news_content)).setText(content);


    }
}

这里提供了一个 refresh 方法,用于将新闻的标题、图片与内容显示在界面中。

这样就把新闻内容的碎片和布局创建好了,但它们都是运行在双页模式中的,所以我们还需创建一个活动 NewsContentActivity,用于单页模式中。

NewsContentActivity 的 布局文件 activity_news_content.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:orientation="vertical">

    <fragment
        android:id="@+id/news_content_fragment"
        android:name="net.deniro.android.fragmentbestpractice.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></fragment>
</LinearLayout>

这里充分发挥了代码的复用性,直接在布局中引入了 NewsContentFragment,这相当于把 news_content_frag 布局也自动加载了进来。

然后编写 NewsContentActivity 的代码:

public class NewsContentActivity extends AppCompatActivity {

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


        //获取传入的数据
        Intent intent = getIntent();
        String title = intent.getStringExtra("title");
        String content = intent.getStringExtra("content");
        int imageResId = intent.getIntExtra("imageResId", 0);

        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
        ((NewsContentFragment) fragment).refresh(title, content, imageResId);
    }

    /**
     * 启动活动
     *
     * @param context
     * @param title      标题
     * @param content    内容
     * @param imageResId 图片资源 ID
     */
    public static void start(Context context, String title, String content, int imageResId) {
        Intent intent = new Intent(context, NewsContentActivity.class);
        intent.putExtra("title", title);
        intent.putExtra("content", content);
        intent.putExtra("imageResId", imageResId);
        context.startActivity(intent);
    }


}

在 onCreate() 方法中,我们通过 Intent 获取传入的新闻标题、图片(这里为了简便,只取出图片列表的第一张图片)和内容,然后调用 FragmentManager 的 findFragmentById() 方法得到 NewsContentFragment 的实例,接着调用它的 refresh() 方法,传入相应的数据。

这里还为 Activity 定义了一个静态的启动方法 start 。

接着,创建显示新闻列表的布局 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: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>

这里只定义了一个用于显示新闻列表的 RecyclerView。

然后,新建 news_item.xml 作为 RecyclerView 子项的布局:

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


    <TextView
        android:id="@+id/news_title"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:gravity="left"
        android:paddingTop="40dp"
        android:paddingLeft="15dp"
        android:paddingRight="30dp"
        android:textSize="20sp"
        android:textColor="#000"
        >
    </TextView>



    <ImageView
        android:id="@+id/news_image"
        android:layout_width="130dp"
        android:layout_height="130dp"
        android:layout_gravity="right"
        android:paddingRight="15dp"
        />

    <!--分割线-->
    <View android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#d0d0d0"/>
</FrameLayout>

这里的布局采用 FrameLayout,然后使用 layout_gravity 来控制控件在布局中的对齐方式。

因为新闻列表和子项布局都创建好了,所以接下来就需要创建一个 NewsTitleFragment 作为展示新闻列表的碎片:

public class NewsTitleFragment extends Fragment {

    private static final String TAG = "NewsTitleFragment";

    /**
     * 是否为双页模式
     */
    private boolean isTwoPage;

    class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
        private List<News> newsList;


        class ViewHolder extends RecyclerView.ViewHolder {
            ImageView image;
            TextView titleText;

            public ViewHolder(View itemView) {
                super(itemView);
                titleText = (TextView) itemView.findViewById(R.id.news_title);
                image = (ImageView) itemView.findViewById(R.id.news_image);
            }
        }

        public NewsAdapter(List<News> data) {
            newsList = data;
            Log.d(TAG, "NewsAdapter: " + 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 = newsList.get(holder.getAdapterPosition());
                    Integer firstImageResId = news.getImages().get(0);
                    if (isTwoPage) {//双页模式,则直接刷新 NewsContentFragment 中的内容
                        ((NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment)).refresh(news.getTitle(), news.getContent(), firstImageResId);
                    } else {//单页模式,直接启动 NewsContentActivity 活动
                        NewsContentActivity.start(getActivity(), news.getTitle(), news.getContent(), firstImageResId);
                    }
                }
            });
            return holder;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            News news = newsList.get(position);
            holder.titleText.setText(news.getTitle());
            holder.image.setImageResource(news.getImages().get(0));//取第一张图片
        }

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

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

        //为 RecyclerView 填充数据
        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(new NewsAdapter(getNews()));

        return view;
    }

    private List<News> getNews() {
        List<News> list = new ArrayList<>();
        addNews(list, R.drawable.new1, "在韩国上演的北京8分钟,中国首次用AI展示国家形象", "时隔14年后,奥林匹克再次进入北京时间。 刚刚结束的平昌冬奥会闭幕仪式上,张艺谋和团队用8分钟的时间,展示了国家形象并向全球发出邀请。与上次雅典8分钟不同的是,这次中国的形象有了更多科技元素。可能你不知道,整个展示过程其实是由人工智能和人类演员共同完成。今晚20点20分,2名大熊猫特使、22名北体大学生滑冰登场,紧随人类表演者亮相的还有24面冰雪通透的屏幕,按照长城砖比例设计,由24个机器人操作。整个表演也因此呈现“画中画”模式。");
        addNews(list, R.drawable.new2, "AI的乌托邦!谷歌母公司正在建造超级智慧城市 ", "xxx");
        addNews(list, R.drawable.new3, "诺基亚CEO:世界各大运营商加速 5G部署将提前一年 ", "xxx");
        addNews(list, R.drawable.new4, "买房难,苹果新总部附近房屋平均售价116万美元 ", "xxx");
        addNews(list, R.drawable.new5, "人民日报三问区块链:区分是技术创新还是集资创新 ", "xxx");
        Log.d(TAG, "getNews: " + list);
        return list;

    }

    /**
     * 新增新闻
     *
     * @param list
     * @param image   图片资源
     * @param title   标题
     * @param content 内容
     */
    private void addNews(List<News> list, int image, String title, String content) {
        News news = new News(title, content);
        List<Integer> images = new ArrayList<>();
        images.add(image);
        news.setImages(images);
        list.add(news);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (getActivity().findViewById(R.id.news_content_layout) != null) {//双页模式
            isTwoPage = true;
        } else {
            isTwoPage = false;
        }
    }
}
  1. 这里新建了一个内部类 NewsAdapter 来作为 RecyclerView 的适配器,因为内部类可以直接访问外部类的变量。

  2. 在 onCreateViewHolder 中,我们注册了点击事件,并根据当前所处的模式,来进行相应的逻辑处理。如果是单页模式,就启动一个新的活动来显示新闻内容;如果是双页模式,就更新新闻内容碎片中的内容。

  3. 在 onCreateView 方法中,把数据填充到 RecyclerView 中。

  4. 在 onActivityCreated 方法中,我们通过能否找到一个 news_content_layout 的 View 来判断当前处于的模式(双页或单页)。这是通过限定符来实现的:

主布局 activity_main.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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="net.deniro.android.fragmentbestpractice.NewsTitleFragment"
        />

</FrameLayout>

接着,在 res 目录下新建 layout-sw600dp 文件夹,并在这个文件夹下再新建一个布局
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:orientation="horizontal">

   <fragment
       android:id="@+id/news_title_fragment"
       android:name="net.deniro.android.fragmentbestpractice.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="2"
        >
        <fragment
            android:id="@+id/news_content_fragment"
            android:name="net.deniro.android.fragmentbestpractice.NewsContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </FrameLayout>


</LinearLayout>

这样当运行在屏幕宽度大于 600 dp 的设备上时,就会加载 news_content_fragment 布局,这样我们也就能判断出当前活动所处的模式啦O(∩_∩)O~

在手机模拟器中运行程序:


手机模拟器中的新闻列表

点击一条新闻:

手机模拟器中的新闻内容

然后在平板模拟器中运行程序:


平板模拟器运行效果

是不是很酷呀O(∩_∩)O~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • 本篇文章主要介绍以下几个知识点:碎片 fragment 的用法;碎片 fragment 的生命周期;动态加载布局的...
    开心wonderful阅读 1,447评论 3 7
  • 一、News 实体类: 二、新闻列表中子项的布局 news_item.xml: 这段代码也非常简单,只是在 Lin...
    TTTqiu阅读 1,248评论 1 1
  • 平板电脑和手机最大的区别就是屏幕的大小不同,一般手机屏幕的大小会在3英寸到6英寸之间,而一般平板电脑屏幕的大小会在...
    付凯强阅读 801评论 0 0
  • 漏夜里,风雪至,恰应和,庭前梅枝。不忍折梅枝,不堪扰风雪,便静立庭前看花,待风雪染尽,融入这天地。不觉凉,不畏寒,...
    矢夭阅读 1,240评论 14 19