企業(yè)一般都會(huì)為應(yīng)用提供手機(jī)版與 Pad 版的程序畴蒲,我們可以利用 Android 碎片悠鞍,編寫出兼容手機(jī)與平板的應(yīng)用程序。
因?yàn)樾侣劻斜頃?huì)用到 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'
}
接下來咖祭,編寫一個(gè)新聞的實(shí)體類:
public class News {
/**
* 標(biāo)題
*/
private String title;
/**
* 內(nèi)容
*/
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 +
'}';
}
}
新聞的實(shí)體類包含標(biāo)題、內(nèi)容和圖片資源列表蔫骂。
新建布局 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"
>
<!--標(biāo)題-->
<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"
/>
<!--內(nèi)容-->
<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>
新聞內(nèi)容的布局主要分為兩個(gè)部分么翰,頭部顯示新聞標(biāo)題,正文顯示新聞圖片與內(nèi)容辽旋,中間使用一條細(xì)線分隔開浩嫌。細(xì)線采用 View 實(shí)現(xiàn)檐迟,把 View的寬與高都設(shè)置為 1dp,然后再通過 background 設(shè)置背景色即可码耐。
然后再新建一個(gè) NewsContentFragment 類追迟,作為新聞內(nèi)容的碎片:
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 標(biāo)題
* @param content 內(nèi)容
* @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);
}
}
這里提供了一個(gè) refresh 方法,用于將新聞的標(biāo)題伐坏、圖片與內(nèi)容顯示在界面中怔匣。
這樣就把新聞內(nèi)容的碎片和布局創(chuàng)建好了,但它們都是運(yùn)行在雙頁模式中的桦沉,所以我們還需創(chuàng)建一個(gè)活動(dòng) 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>
這里充分發(fā)揮了代碼的復(fù)用性纯露,直接在布局中引入了 NewsContentFragment剿骨,這相當(dāng)于把 news_content_frag 布局也自動(dòng)加載了進(jìn)來。
然后編寫 NewsContentActivity 的代碼:
public class NewsContentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_content);
//獲取傳入的數(shù)據(jù)
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);
}
/**
* 啟動(dòng)活動(dòng)
*
* @param context
* @param title 標(biāo)題
* @param content 內(nèi)容
* @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 獲取傳入的新聞標(biāo)題浓利、圖片(這里為了簡便,只取出圖片列表的第一張圖片)和內(nèi)容钞速,然后調(diào)用 FragmentManager 的 findFragmentById() 方法得到 NewsContentFragment 的實(shí)例贷掖,接著調(diào)用它的 refresh() 方法,傳入相應(yīng)的數(shù)據(jù)渴语。
這里還為 Activity 定義了一個(gè)靜態(tài)的啟動(dòng)方法 start 苹威。
接著,創(chuàng)建顯示新聞列表的布局 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>
這里只定義了一個(gè)用于顯示新聞列表的 RecyclerView驾凶。
然后牙甫,新建 news_item.xml 作為 RecyclerView 子項(xiàng)的布局:
<?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 來控制控件在布局中的對(duì)齊方式调违。
因?yàn)樾侣劻斜砗妥禹?xiàng)布局都創(chuàng)建好了窟哺,所以接下來就需要?jiǎng)?chuàng)建一個(gè) 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 中的內(nèi)容
((NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment)).refresh(news.getTitle(), news.getContent(), firstImageResId);
} else {//單頁模式技肩,直接啟動(dòng) NewsContentActivity 活動(dòng)
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 填充數(shù)據(jù)
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展示國家形象", "時(shí)隔14年后,奧林匹克再次進(jìn)入北京時(shí)間虚婿≈掣妫 剛剛結(jié)束的平昌冬奧會(huì)閉幕儀式上,張藝謀和團(tuán)隊(duì)用8分鐘的時(shí)間雳锋,展示了國家形象并向全球發(fā)出邀請(qǐng)黄绩。與上次雅典8分鐘不同的是,這次中國的形象有了更多科技元素玷过∷ぃ可能你不知道筑煮,整個(gè)展示過程其實(shí)是由人工智能和人類演員共同完成。今晚20點(diǎn)20分粤蝎,2名大熊貓?zhí)厥埂?2名北體大學(xué)生滑冰登場真仲,緊隨人類表演者亮相的還有24面冰雪通透的屏幕,按照長城磚比例設(shè)計(jì)初澎,由24個(gè)機(jī)器人操作秸应。整個(gè)表演也因此呈現(xiàn)“畫中畫”模式。");
addNews(list, R.drawable.new2, "AI的烏托邦碑宴!谷歌母公司正在建造超級(jí)智慧城市 ", "xxx");
addNews(list, R.drawable.new3, "諾基亞CEO:世界各大運(yùn)營商加速 5G部署將提前一年 ", "xxx");
addNews(list, R.drawable.new4, "買房難软啼,蘋果新總部附近房屋平均售價(jià)116萬美元 ", "xxx");
addNews(list, R.drawable.new5, "人民日?qǐng)?bào)三問區(qū)塊鏈:區(qū)分是技術(shù)創(chuàng)新還是集資創(chuàng)新 ", "xxx");
Log.d(TAG, "getNews: " + list);
return list;
}
/**
* 新增新聞
*
* @param list
* @param image 圖片資源
* @param title 標(biāo)題
* @param content 內(nèi)容
*/
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;
}
}
}
這里新建了一個(gè)內(nèi)部類 NewsAdapter 來作為 RecyclerView 的適配器,因?yàn)閮?nèi)部類可以直接訪問外部類的變量延柠。
在 onCreateViewHolder 中祸挪,我們注冊(cè)了點(diǎn)擊事件,并根據(jù)當(dāng)前所處的模式贞间,來進(jìn)行相應(yīng)的邏輯處理贿条。如果是單頁模式,就啟動(dòng)一個(gè)新的活動(dòng)來顯示新聞內(nèi)容增热;如果是雙頁模式整以,就更新新聞內(nèi)容碎片中的內(nèi)容。
在 onCreateView 方法中峻仇,把數(shù)據(jù)填充到 RecyclerView 中公黑。
在 onActivityCreated 方法中,我們通過能否找到一個(gè) news_content_layout 的 View 來判斷當(dāng)前處于的模式(雙頁或單頁)础浮。這是通過限定符來實(shí)現(xiàn)的:
主布局 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 文件夾奠骄,并在這個(gè)文件夾下再新建一個(gè)布局
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>
這樣當(dāng)運(yùn)行在屏幕寬度大于 600 dp 的設(shè)備上時(shí)豆同,就會(huì)加載 news_content_fragment 布局,這樣我們也就能判斷出當(dāng)前活動(dòng)所處的模式啦O(∩_∩)O~
在手機(jī)模擬器中運(yùn)行程序:
點(diǎn)擊一條新聞:
然后在平板模擬器中運(yùn)行程序:
是不是很酷呀O(∩_∩)O~