用户界面将由一个名为
CrimeFragment
的
UI fragment
进行管理。
CrimeFragment
的
实例将通过一个名为
CrimeActivity
的
activity
来
托管
。
CrimeActivity
视图由
FrameLayout
组件组成,
FrameLayout
组件为
CrimeFragment
要显示
的视图安排了存放位置。
CrimeFragment
的视图由一个
LinearLayout
组件及一个
EditText
组件组成。
CrimeFragment
类中有一个存储
EditText
的成员变量(
mTitleField
)。
mTitleField
上设有监
听器,当
EditText
上的文字发生改变时,用来更新模型层的数据。
FragmentActivity
是
Activity
的子类,具有新系统版本
Activity
类管理
fragment
的能力,即便是在较早版本的
Android
设备上也可对
fragment
进行管理。
创建CrimeActivity
托管 UI fragment
为托管
UI fragment
,
activity
必须做到:
在布局中为fragment
的视图安排位置;
管理fragment
实例的生命周期。
定义容器视图
修改activity_crime.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragmentContainer">
</FrameLayout>
创建 UI fragment
fragment
视图的布局文件(
fragment_crime.xml
)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".CrimeFragment">
<EditText
android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入标题" />
</FrameLayout>
创建CrimeFragment
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import yu.app.criminalintent.model.Crime;
/**
*
*/
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime=new Crime();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragment_crime, container, false);
mTitleField = v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence c, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence c, int start, int before, int count) {
mCrime.setmTitle(c.toString());
Toast.makeText(getContext(), c.toString(), Toast.LENGTH_SHORT).show();
}
@Override
public void afterTextChanged(Editable editable) {
}
});
// Inflate the layout for this fragment
return v;
}
}
Fragment.onCreateView(...)
方法中的组件引用几乎等同于
Activity.onCreate(...)
方法的处理。唯一的区别是我们调用了当前
fragment
视图的
View.findViewById(int)
方法。以前使
用的
Activity.findViewById(int)
方法十分便利,能够在后台自动调用
View.findView
ById(int)
方法。而
Fragment
类没有对应的便利方法,因此我们必须自己完成调用。
fragment
中监听器方法的设置和
activity
中的处理完全一样。
创建实现
TextWatcher
监听器接口的匿名内部类。
TextWatcher
有三种方法,不过我们现在只需关注其中
的
onTextChanged(...)
方法。
在
onTextChanged(...)
方法中,调用
CharSequence
(代表用户输入)的
toString()
方法。
该方法最后返回用来设置
Crime
标题的字符串。
CrimeFragment
类的代码实现部分完成了。但现在还不能运行应用查看用户界面和检验代
码。因为
fragment
无法将自己的视图显示在屏幕上。接下来我们首先要把
CrimeFragment
添加给
CrimeActivity。
添加 UI fragment 到 FragmentManager
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import android.os.Bundle;
public class CrimeActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
//获取碎片管理
FragmentManager fm = getSupportFragmentManager();
//获取碎片存放的容器
Fragment f = fm.findFragmentById(R.id.fragmentContainer);
if (f==null){
//实例化碎片
f=new CrimeFragment();
//碎片管理开启事务添加实例化碎片到碎片存放容器中并提交事务
fm.beginTransaction().add(R.id.fragmentContainer, f).commit();
}
}
}
fragment
事务被用来添加、移除、附加、分离或替换
fragment
队列中的
fragment
。这是使用
fragment
在运行时组装和重新组装用户界面的核心方式。
FragmentManager
管理着
fragment
事务
的回退栈。
FragmentManager.beginTransaction()
方法创建并返回
FragmentTransaction
实例。
FragmentTransaction
类使用了一个
fluent interface
接口方法,通过该方法配置
FragmentTran
saction
返回
FragmentTransaction
类对象,而不是
void
,由此可得到一个
FragmentTransa
ction队列。
add(...)
方法是整个事务的核心部分,并含有两个参数,即容器视图资源
ID
和新创建的
CrimeFragment
。容器视图资源
ID
我们应该很熟悉了,它是定义在
activity_crime.xml
中的
FrameLayout
组件的资源
ID
。容器视图资源
ID
主要有两点作用:
告知
FragmentManager
fragment
视图应该出现在
activity
视图的什么地方;
是
FragmentManager
队列中
fragment
的唯一标识符。
升级Crime类
import java.util.Date;
import java.util.UUID;
public class Crime {
private UUID mID;
private String mTitle;
private Date mDate;
private boolean mSolved;
public Crime(){
//生成唯一标识符
mID=UUID.randomUUID();
mDate=new Date();
}
public UUID getmID() {
return mID;
}
public String getmTitle() {
return mTitle;
}
public void setmTitle(String mTitle) {
this.mTitle = mTitle;
}
public Date getmDate() {
return mDate;
}
public void setmDate(Date mDate) {
this.mDate = mDate;
}
public boolean ismSolved() {
return mSolved;
}
public void setmSolved(boolean mSolved) {
this.mSolved = mSolved;
}
}
更新fragment_crime.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".CrimeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/mCrime_title"
style="?android:listSeparatorTextViewStyle"/>
<EditText
android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:hint="@string/crime_title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_detail"
style="?android:listSeparatorTextViewStyle"/>
<Button
android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
<CheckBox
android:id="@+id/crime_solved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="@string/isSolved"/>
</LinearLayout>
</FrameLayout>
更新CrimeFragment
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import yu.app.criminalintent.model.Crime;
/**
*
*/
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
private Button mDateBtn;
private CheckBox mSolvedCbox;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime=new Crime();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragment_crime, container, false);
mTitleField = v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence c, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence c, int start, int before, int count) {
mCrime.setmTitle(c.toString());
Toast.makeText(getContext(), c.toString(), Toast.LENGTH_SHORT).show();
}
@Override
public void afterTextChanged(Editable editable) {
}
});
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd E a HH:mm:ss");
mDateBtn = v.findViewById(R.id.crime_date);
mDateBtn.setText(dateFormat.format(mCrime.getmDate()));
mDateBtn.setEnabled(false);
mSolvedCbox = v.findViewById(R.id.crime_solved);
mSolvedCbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
mCrime.setmSolved(b);
Toast.makeText(getContext(), String.valueOf(mCrime.ismSolved()) , Toast.LENGTH_SHORT).show();
}
});
// Inflate the layout for this fragment
return v;
}
}
使用ListFragment显示列表
应用的模型层将新增一个
CrimeLab
对象,该对象是一个数据集中存储池,用来存储
Crime
对象。
显示
crime
列表需在应用的控制层新增一个
activity
和一个
fragment
,即
CrimeListActivity
和
CrimeListFragment
。
CrimeListFragment
是
ListFragment
的子类,
ListFragment
是
Fragment
的子类。
Fragment
内置列表显示支持功能。控制层对象间、控制层对象与
CrimeLab
对象间彼此交互,进
行模型层数据的存取。
ArrayList<E>
是一个支持存放指定数据类型对象的
Java
有序数组类,具有获取、新增及删除
数组中元素的方法。
创建CrimeLab
import android.content.Context;
import java.util.ArrayList;
import java.util.UUID;
public class CrimeLab {
private ArrayList<Crime> mCrimes;
private static CrimeLab crimeLab;
private Context appContext;
private CrimeLab(Context appContext){
this.appContext=appContext;
this.mCrimes=new ArrayList<>();
//测试 先往数组列表中批量存入100个Crime对象
for (int i=1;i<=100;i++){
Crime c=new Crime();
c.setmTitle("Crime标题#"+i);
c.setmSolved(i%2==0);
mCrimes.add(c);
}
}
public static CrimeLab get(Context c){
if (crimeLab==null){
crimeLab = new CrimeLab(c.getApplicationContext());
}
return crimeLab;
}
public ArrayList<Crime> getmCrimes() {
return mCrimes;
}
public Crime getCrime(UUID id){
for (Crime c : mCrimes){
if (c.getmID().equals(id)){
return c;
}
}
return null;
}
}
创建CrimeListActivity
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import yu.app.criminalintent.base.SingleFragmentActivity;
import yu.app.criminalintent.fragment.CrimeListFragment;
public class CrimeListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list_list, container, false);
return view;
}
}
创建CrimeListFragment
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.ListFragment;
import java.util.ArrayList;
import yu.app.criminalintent.R;
import yu.app.criminalintent.base.SingleFragmentActivity;
import yu.app.criminalintent.model.Crime;
import yu.app.criminalintent.model.CrimeLab;
/**
* A fragment representing a list of Items.
*/
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
private ArrayList<Crime> mCrimes;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrimes = CrimeLab.get(getActivity()).getmCrimes();
ArrayAdapter<Crime> adapter = new ArrayAdapter<Crime>(getActivity(), android.R.layout.simple_expandable_list_item_1, mCrimes);
setListAdapter(adapter);
}
@Override
public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Crime c = (Crime) getListAdapter().getItem(position);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("提示信息");
builder.setMessage(c.toString());
builder.setPositiveButton("确定",new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
});
AlertDialog alertDialog=builder.create();
alertDialog.show();
}
}
修改AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.YuApp">
<activity
android:name=".CrimeListActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
</application>
</manifest>
fragment_crime_list_list.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView 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/list"
android:name="yu.app.criminalintent.fragment.CrimeListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragment.CrimeListFragment"
tools:listitem="@layout/fragment_crime_list" />
创建SingleFragmentActivity
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import yu.app.criminalintent.R;
/**
* 通用fragment的抽象类
*/
public abstract class SingleFragmentActivity extends FragmentActivity {
//该抽象方法可实例化新的fragment,SingleFragmentActivity的子类会实现该方法返回一个由activity托管的fragment实例
protected abstract Fragment createFragment();
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
FragmentManager fm=getSupportFragmentManager();
Fragment f = fm.findFragmentById(R.id.fragmentContainer);
if (f==null){
f=createFragment();
fm.beginTransaction().add(R.id.fragmentContainer, f).commit();
}
}
public abstract View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState);
}