说说 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~

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

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

友情链接更多精彩内容