Android Fragment實踐之簡易的新聞應用

基本概念

Fragment,簡稱碎片,是Android 3.0(API 11)提出的,爲了兼容低版本,support-v4庫中也開發了一套Fragment API,最低兼容Android 1.6。

過去support-v4庫是一個jar包,24.2.0版本開始,將support-v4庫模塊化爲多個jar包,包含:support-fragment, support-ui, support-media-compat等,這麼做是爲了減少APK包大小,你需要用哪個模塊就引入哪個模塊。

如果想引入整個support-v4庫,則compile 'com.android.support:support-v4:24.2.1',如果只想引入support-fragment庫,則com.android.support:support-fragment:24.2.1

  • Fragment是依賴於Activity的,不能獨立存在的。
  • 一個Activity裏可以有多個Fragment。
  • 一個Fragment可以被多個Activity重用。
  • Fragment有自己的生命週期,並能接收輸入事件。
  • 我們能在Activity運行時動態地添加或刪除Fragment。

Fragment的優勢有以下幾點:

  • 模塊化(Modularity):我們不必把所有代碼全部寫在Activity中,而是把代碼寫在各自的Fragment中。
  • 可重用(Reusability):多個Activity可以重用一個Fragment。
  • 可適配(Adaptability):根據硬件的屏幕尺寸、屏幕方向,能夠方便地實現不同的佈局,這樣用戶體驗更好。

Demo:FragmentBestPractice兼容平板和手機的一個簡易的新聞應用

View層:

雙頁模式下新聞內容部分的佈局:news_fragment_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:gravity="center_horizontal|center_vertical"
            android:layout_width="match_parent"
            android:layout_height="50dp" />
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000"/>
        <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="#000"
        android:layout_alignParentStart="true" />
</RelativeLayout>

單頁模式下新聞內容部分的佈局activity_news_content.xml:

<?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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Control.NewsContentActivity">

    <fragment
        android:id="@+id/news_content_fragment"
        android:name="com.example.dpl.fragmentbestpractice.Control.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

兩種模式下共用新聞標題列表的佈局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:orientation="vertical">


    <android.support.v7.widget.RecyclerView
        android:id="@+id/news_title_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal"
        android:background="#1C1C1C" />
</LinearLayout>

 新聞標題列表的子佈局news_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/news_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:maxLines="1"
    android:ellipsize="end"
    android:textSize="25sp"
    android:textColor="#fff"
    android:paddingRight="10dp"
    android:paddingLeft="10dp"
    android:paddingBottom="15dp"
    android:paddingTop="15dp"
    />

 單頁模式下,只加載一個新聞標題的碎片佈局:activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<!--單頁模式頁面碎片-->
<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=".Control.MainActivity">

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

</FrameLayout>

 通過限定符的方式系統區分運行的大屏幕還是小屏幕,新建適配大屏幕的佈局文件夾layout-sw600dp,在其下創建activity_main.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"
    android:baselineAligned="false">

    <fragment
        android:id="@+id/news_title_fragment"
        android:name="com.example.dpl.fragmentbestpractice.Control.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="com.example.dpl.fragmentbestpractice.Control.NewsContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </FrameLayout>
</LinearLayout>

Model層: 

 新聞類:News.java

public class News {
    private String title;
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
}

 Control層:

創建兩種模式下共用新聞內容的Fragment:NewsContentFragment.java:

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 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);//刷新內容
    }
}

 單頁模式下引用其上Fragment的Activity:NewsContentActivity.java:

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.activity_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界面
    }
}

兩種模式下新聞標題部分的碎片NewsTitleFragment.java:

public class NewsTitleFragment extends Fragment {
    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);
        RecyclerView newsTitleRecyclerView= (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
        LinearLayoutManager manager=new LinearLayoutManager(view.getContext());
        newsTitleRecyclerView.setLayoutManager(manager);
        NewsAdapter newsAdapter=new NewsAdapter(getNews());
        newsTitleRecyclerView.setAdapter(newsAdapter);
        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 the title"+i);
            news.setContent(getRandomLengthContent("This is new 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; //可以找到news_content_layout時,顯示雙頁模式
        } else {
            isTwoPane = false;//找不到new_content_layout時,顯示單頁模式
        }
    }

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

        class ViewHolder extends RecyclerView.ViewHolder {
            TextView newsTitleText;

            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                newsTitleText = (TextView) itemView.findViewById(R.id.news_title);
            }
        }

        private NewsAdapter(List<News> newsList) {
            mNewsList = newsList;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final int i) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.news_item, viewGroup, 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 = (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(@NonNull ViewHolder viewHolder, int i) {
             News news=mNewsList.get(i);
             viewHolder.newsTitleText.setText(news.getTitle());
        }

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

 主活動MainActivity.java:

public class MainActivity extends AppCompatActivity {

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

運行效果:

           

Github  源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章