Android學習第7篇——碎片實踐,結合ListView的簡單閱讀應用,自適應手機和平板
在學過了碎片(Fragment)、ListView之後,實現一個自適應手機和平板的文章閱讀應用
效果圖:
手機:
平板:
二、實現過程:
1、新建一個文章實體類News
public class News { private String title; private String content; 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; } }
包含標題和內容;
2、接著新建一個 news_item.xml 佈局,用於作為文章列表中子項的佈局:
android:singleLine 設定為 true 表示讓這個 TextView 只能單行顯示。android:ellipsize 用於設定當文字內容超出控<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/news_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:ellipsize="end" android:textSize="18sp" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="15dp" android:paddingBottom="15dp" /> </LinearLayout>
件寬度時,文字的縮略方式,這裡指定成 end 表示在尾部進行縮略。
3、接下來需要建立文章列表的介面卡,讓這個介面卡繼承自 ArrayAdapter,並將泛型指定為 News 類
package com.csii.www.fragmentnews.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import com.csii.www.fragmentnews.R; import com.csii.www.fragmentnews.entity.News; import java.util.List; /** * Created by zhang on 2018/3/27. */ public class NewsAdapter extends ArrayAdapter<News> { private int resourceId; public NewsAdapter(Context context, int textViewResourceId, List<News> objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { News news = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, null); } else { view = convertView; } TextView newsTitleText = (TextView) view.findViewById(R.id.news_title); newsTitleText.setText(news.getTitle()); return view; } }
可以看到,在 getView()方法中,我們獲取到了相應位置上的 News 類,並讓文章的標題在列表中進行顯示。
4、這樣基本就把文章列表部分的程式碼編寫完了,接下來我們看一下如何編寫新聞內容部分的程式碼。新建佈局檔案的程式碼。新建佈局檔案 frag_news_content.xml,程式碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="1dp"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/spilt_line_vertical"
/>
<LinearLayout
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
android:orientation="vertical">
<TextView
android:id="@+id/newsContentTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:scaleType="fitXY"
android:src="@drawable/spilt_line"
/>
<TextView
android:id="@+id/newsContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp"
/>
</LinearLayout>
</LinearLayout>
</ScrollView>
文章內容的佈局主要可以分為兩個部分,頭部顯示完整的文章標題,正文部分顯示文章內容,中間使用一條細線分隔開。這裡的細線是利用 ImageView 顯示了一張很窄的圖片來實現的,將 ImageView 的 android:scaleType 屬性設定為 fitXY,表示讓這張圖片填充滿整個控制元件的大小。另外外部用了ScrollView佈局,支援文章內容較長是可以滾動閱讀。
5、然後再新建一個 NewsContentFragment 類,繼承自 Fragment,程式碼如下所示:
public class NewsContentFragment extends Fragment{
private View view;
//建立Fragment時候構建一個View
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.frag_news_content,container);
return view;
}
//渲染frament檢視view
public void regresh(News news){
//點選文章的時候頁面ScrollView滾動到頂部,否則頁面在底部,影響體驗
view.scrollTo(0,0);
//設定檢視的屬性顯示
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);
//通過傳送過來的News例項,填充內容。
TextView newsTitle = view.findViewById(R.id.newsContentTitle);
TextView newsContent = view.findViewById(R.id.newsContent);
newsTitle.setText(news.getTitle());
newsContent.setText(news.getContent());
}
}
6、接下來還需要再建立一個用於顯示新聞列表的佈局,新建 news_title_frag.xml,程式碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/newList"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
裡面只有一個 ListView。不過想必你已經猜到了,這個佈局並不是給活動使用的,而是給碎片使用的,因此我們還需要建立一個碎片來載入這個佈局。新建一個 NewsListFragment 類,繼承自 Fragment,程式碼如下所示: public class NewListFragment extends Fragment implements AdapterView.OnItemClickListener{
TitleAdapter titleAdapter;
List newsList = new ArrayList();
boolean isTwoPanel;
//當Fragment和Activity建立聯絡的時候初始化資料
@Override
public void onAttach(Context context) {
super.onAttach(context);
initNews();//初始化新聞資料
titleAdapter = new TitleAdapter(context, R.layout.news_item, newsList);
}
//建立Fragment時候構建一個View
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag_news_list, container, false);//這裡是資原始檔的名稱
ListView titleList = view.findViewById(R.id.newList);
titleList.setAdapter(titleAdapter);
titleList.setOnItemClickListener(this);
return view;
}
//在活動與頁面關聯好後,判斷是手機還是pad
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getActivity().findViewById(R.id.news_content_layout) != null ){
isTwoPanel = true;
}else{
isTwoPanel = false;
}
}
private void initNews() {
newsList.add(new News("誰為新詞譜一曲",
"林花謝,\n" +
"\n" +
"落盡春紅。\n" +
"\n" +
"嘆華年易逝,\n" +
"\n" +
"時光太匆匆。\n" +
"\n" +
"無奈人生,山重水複。\n" +
"\n" +
"人生無奈,水復山重。\n" +
"\n" +
"清早間瀟瀟冷雨,\n" +
"\n" +
"到晚來悽悽寒風。\n" +
"\n" +
"紅顏一去,\n" +
"\n" +
"只留下,\n" +
"\n" +
"醉眼中搖曳的倩影;\n" +
"\n" +
"江山傾倒,\n" +
"\n" +
"只留下,\n" +
"\n" +
"心田裡胭脂上淚痕。\n" +
"\n" +
"何時入夢裡?\n" +
"\n" +
"與君相逢;\n" +
"\n" +
"何時入夢裡?\n" +
"\n" +
"與君重逢。\n" +
"\n" +
"怕只怕,\n" +
"\n" +
"人生空餘恨,似水東流;\n" +
"\n" +
"怕只怕,\n" +
"\n" +
"人生空餘恨,似水長流。"));
newsList.add(new News("國慶節出行", "好急啊"));
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
News news = (News)newsList.get(position);
if(isTwoPanel == true ){//雙屏顯示,點選文章標題重新整理右邊碎片
//通過getFragmentManager(),然後fundFragmentById()獲取頁面中的Fragment
NewsContentFragment newContentFragment = (NewsContentFragment)getFragmentManager().findFragmentById(R.id.newContentFragment);
//將New實體傳送過去
newContentFragment.regresh(news);
}else{
//如果是手機,直接啟動下一個Activity顯示文章內容。
NewsContentActivity.startAction(getActivity(),news);
}
}
}
根據碎片的生命週期,我們知道,onAttach()方法會首先執行,因此在這裡做了一些資料初始化的操作,比如呼叫 getNews()方法獲取幾條模擬的文章資料,以及完成 NewsAdapter 的建立。然後在 onCreateView()方法中載入了frag_news_list 佈局, 並給新聞列表的 ListView 註冊了點選事件。 接下來在 onActivityCreated()方法中,我們通過是否能夠找到一個 id 為 news_content_layout 的 View 來判斷當前是雙頁模式還是單頁模式,這個 id 為 news_content_layout 的 View 只在雙頁模式中才會出現,在稍後的佈局裡你將會看到。然後在 ListView 的點選事件裡我們就可以判斷,如果當前是單頁模式,就啟動一個新的活動去顯示新聞內容,如果當前是雙頁模式,就更新文章內容碎片裡的資料。
7、修改 activity_main.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:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.csii.www.newsfragment.MainActivity">
<fragment
android:id="@+id/newListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.csii.www.newsfragment.fragment.NewListFragment"
></fragment>
</LinearLayout>
上述程式碼表示, 在單頁模式下, 只會載入一個新聞標題的碎片 。然後新建 layout-sw600dp資料夾,在這個資料夾下再新建一個 activity_main.xml 檔案,程式碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.csii.www.newsfragment.MainActivity">
<fragment
android:id="@+id/newListFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:name="com.csii.www.newsfragment.fragment.NewListFragment"
></fragment>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="match_parent">
<fragment
android:id="@+id/newContentFragment"
android:name="com.csii.www.newsfragment.fragment.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</fragment>
</FrameLayout>
</LinearLayout>
可以看出,在雙頁模式下我們同時引入了兩個碎片,並將新聞內容的碎片放在了一個FrameLayout 佈局下,而這個佈局的 id 正是 news_content_layout。因此,能夠找到這個 id 的時候就是雙頁模式,否則就是單面模式。
8、然後新建 NewsContentActivity,作為顯示新聞內容的活動,程式碼如下所示:
package com.csii.www.newsfragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.csii.www.newsfragment.entity.News;
public class NewsContentActivity extends AppCompatActivity {
public static void startAction(Context context, News news){
Intent intent = new Intent(context,NewsContentActivity.class);
intent.putExtra("newsTitle",news.getTitle());
intent.putExtra("newsContent",news.getContent());
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_content);
Intent intent = getIntent();
String newsTitle = intent.getStringExtra("newsTitle");
String newsContent = intent.getStringExtra("newsContent");
TextView newsTitleView =(TextView) findViewById(R.id.newsContentTitle);
TextView newsContentView =(TextView) findViewById(R.id.newsContent);
newsTitleView.setText(newsTitle);
newsContentView.setText(newsContent);
}
}
對應佈局檔案:
<?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="com.csii.www.newsfragment.NewsContentActivity">
<TextView
android:id="@+id/newsContentTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:scaleType="fitXY"
android:background="#000"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1">
<TextView
android:id="@+id/newsContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:padding="15dp"
android:layout_weight="1"
android:textSize="18sp"
/>
</ScrollView>
</LinearLayout>
這裡只對文章展示部分滑動,標題置頂顯示。