Android Fragment基础学习

    技术2022-07-10  192

    Fragment的一个小例子

    代码核心思想优化

    希望实现一个这样的应用,在一个Activity的左侧有一个列表,点击列表中的项,右侧就会显示对应项的详情页,如下图,本例中用接口来实现Fragment之间的通信

    代码

    设计一个MovieModel类模拟系统的数据模型

    package com.example.fragmentsetting.practice.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MovieModel { public static class Movie{ Integer id; String name; String desc; public Movie(Integer id, String name, String desc) { this.id = id; this.name = name; this.desc = desc; } @Override public String toString() { return name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } public static List<Movie> MOVIES=new ArrayList<>(); public static Map<Integer,Movie> MOVIEMAP=new HashMap<>(); static{ Movie m1=new Movie(1,"阿凡达", "《阿凡达》(Avatar)是一部由詹姆斯·卡梅隆执导,二十世纪福克斯出品," + "萨姆·沃辛顿、佐伊·索尔达娜和西格妮·韦弗等人主演的科幻电影。"); Movie m2=new Movie(2,"星际穿越", "《星际穿越》(Interstellar)是克里斯托弗·诺兰执导的一部原创科幻冒险电影," + "由马修·麦康纳、安妮·海瑟薇、杰西卡·查斯坦及迈克尔·凯恩主演。"); Movie m3=new Movie(3,"盗梦空间", "《盗梦空间》是由克里斯托弗·诺兰执导," + "莱昂纳多·迪卡普里奥,玛丽昂·歌迪亚等主演的电影。影片剧情游走于梦境与现实之间,被定义为“发生在意识结构内的当代动作科幻片”。"); add(m1); add(m2); add(m3); } private static void add(Movie movie){ MOVIES.add(movie); MOVIEMAP.put(movie.getId(), movie); } }

    activity_two_pane.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" android:orientation="horizontal" android:divider="?android:attr/dividerHorizontal" android:showDividers="middle" tools:context=".practice.TwoPaneActivity"> <fragment android:id="@+id/list" android:name="com.example.fragmentsetting.practice.fragment.MovieListFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> <FrameLayout android:id="@+id/detail" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="2"/> </LinearLayout>

    TwoPaneActivity.java 显示两个Fragment

    package com.example.fragmentsetting.practice; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import com.example.fragmentsetting.R; import com.example.fragmentsetting.practice.fragment.DetailFragment; import com.example.fragmentsetting.practice.fragment.MovieListFragment; public class TwoPaneActivity extends AppCompatActivity implements MovieListFragment.Callbacks { FragmentManager manager; FragmentTransaction transaction; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_two_pane); manager=getSupportFragmentManager(); transaction=manager.beginTransaction(); } @Override public void onItemSelected(Integer id) { transaction=manager.beginTransaction(); //FragmentTransaction:在commit()方法之前调用addToBackStack方法将事务添加到Back栈, //该栈由Activity管理,允许用户按Back按钮返回Fragment的上一个状态,如果不调用,用户 //点击Back按钮,会直接退出程序 transaction.replace(R.id.detail,new DetailFragment(id)).addToBackStack(null).commit(); } }

    MovieListFragment.java 显示Movie列表

    package com.example.fragmentsetting.practice.fragment; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import androidx.annotation.Nullable; import androidx.fragment.app.ListFragment; import com.example.fragmentsetting.practice.model.MovieModel; public class MovieListFragment extends ListFragment { private Callbacks mCallbacks; public interface Callbacks{ public void onItemSelected(Integer id); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setListAdapter(new ArrayAdapter<MovieModel.Movie>(getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, MovieModel.MOVIES)); } @Override public void onAttach(Activity activity) { super.onAttach(activity); //判断activity是否实现了Callbacks接口,若未实现,则抛出异常 if(!(activity instanceof Callbacks)){ throw new IllegalStateException("Activity没有实现Callbacks接口!"); } mCallbacks=(Callbacks)activity; } @Override public void onDetach() { super.onDetach(); mCallbacks=null; } @Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); mCallbacks.onItemSelected(MovieModel.MOVIES.get(position).getId()); } }

    DetailFragment.java 用于显示Movie详情页

    package com.example.fragmentsetting.practice.fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.example.fragmentsetting.R; import com.example.fragmentsetting.practice.model.MovieModel; public class DetailFragment extends Fragment { private MovieModel.Movie movie; public DetailFragment(Integer id){ movie=MovieModel.MOVIEMAP.get(id); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root=inflater.inflate(R.layout.fragment_detail, container,false); TextView titleTv=root.findViewById(R.id.tv_title); TextView detailTv=root.findViewById(R.id.tv_detail); titleTv.setText(movie.getName()); detailTv.setText(movie.getDesc()); return root; } }

    fragment_detail.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" android:padding="20dp"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:text="title"/> <TextView android:id="@+id/tv_detail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="detail..." android:layout_marginTop="10dp"/> </LinearLayout>

    核心思想

    在MovieListFragment中定义Callbacks内部回调接口,声明一个onItemSelected方法,参数id表示要选中的Movie的id,令TwoPaneActivity实现Callbacks接口并重写其中的onItemSelected方法,将参数id传入DetailFragment的构造函数的参数中使DetailFragment知道它应该加载哪一个Movie的信息,这样就实现了TwoPaneActivity和DetailFragment之间的通信。在MovieListFragment中还要创建一个Callbacks对象,在onAttach方法中使其在onListItemSelected方法中调用该对象的onItemSelected方法,并将当前选中项对应Movie的id传入该方法,这样就实现了MovieListFragment和TwoPaneActivity之间的通信。两个Fragment通过Activity简介实现了通信

    优化

    这样的页面可能更适合大屏幕的平板电脑,但是在小屏幕的手机上,我们更希望一个Activity上是一个单独的列表,选择列表项后跳转到对应的详情Activity 为了兼顾屏幕分辨率,可以创建一个values-large,在该文件夹下建立一个名为practice_refs.xml的引用资源文件,该引用资源专门用于定义各种引用项 practice_refs.xml

    <?xml version="1.0" encoding="utf-8"?> <resources> <!-- 定义activity_list实际引用@layout/activity_two_pane资源 --> <item name="activity_list" type="layout"> @layout/activity_two_pane </item> </resources>

    activity_list.xml

    <?xml version="1.0" encoding="utf-8"?> <!-- 直接使用MovieListFragment作为界面组件 --> <fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/book_list" android:name="com.example.fragmentsetting.practice.fragment.MovieListFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" />

    ListActivity.java 只有列表的Activity

    package com.example.fragmentsetting.practice; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.content.Intent; import android.os.Bundle; import com.example.fragmentsetting.R; import com.example.fragmentsetting.practice.fragment.DetailFragment; import com.example.fragmentsetting.practice.fragment.MovieListFragment; public class ListActivity extends AppCompatActivity implements MovieListFragment.Callbacks { //定义一个旗标,用于标识应用是否支持大屏幕 boolean mTwoPane; FragmentManager manager; FragmentTransaction transaction; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //指定加载R.layout.activity_list对应的界面布局,但实际上会根据屏幕分辨率加载不同的界面布局 setContentView(R.layout.activity_list); //如果加载的布局文件包含id为detail的组件 if(findViewById(R.id.detail)!=null){ mTwoPane=true; manager=getSupportFragmentManager(); ((MovieListFragment)manager.findFragmentById(R.id.list)).setActivateOnItemClick(true); }else{ getSupportActionBar().setTitle("电影列表"); } } @Override public void onItemSelected(Integer id) { if (mTwoPane){ transaction=manager.beginTransaction(); transaction.replace(R.id.detail,new DetailFragment(id)); //将事务添加到Back栈,允许用户按Back键返回到替换Fragment之前的状态 transaction.addToBackStack(null); transaction.commit(); }else{ Intent detailIntent=new Intent(ListActivity.this,DetailActivity.class); detailIntent.putExtra(DetailFragment.ITEM_ID, id); startActivity(detailIntent); } } }

    activity_detail.xml

    <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/detail" android:layout_width="match_parent" android:layout_height="match_parent" />

    DetailActivity.java 显示详情的Activity

    package com.example.fragmentsetting.practice; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; import com.example.fragmentsetting.R; import com.example.fragmentsetting.practice.fragment.DetailFragment; public class DetailActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //指定加载/res/layout目录下的activity_detail.xml布局文件 //该界面布局文件内只定义了一个名为detail的FrameLayout setContentView(R.layout.activity_detail); //将ActionBar上的应用图标转换成可点击的按钮,即提供可返回主页的按钮 getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("电影详情"); int id=getIntent().getIntExtra(DetailFragment.ITEM_ID, 0); getSupportFragmentManager().beginTransaction().add(R.id.detail,new DetailFragment(id)).commit(); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if(item.getItemId()==android.R.id.home){ Intent intent=new Intent(this,ListActivity.class); //添加额外的Flag,将Activity栈中处于FirstActivity之上的Activity弹出 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; } return super.onOptionsItemSelected(item); } }

    MovieListFragment.java 添加如下方法,当小屏幕显示列表占据一整个Activity时使onListItemSelected方法失效

    public void setActivateOnItemClick(boolean activateOnItemClick){ getListView().setChoiceMode(activateOnItemClick?ListView.CHOICE_MODE_SINGLE:ListView.CHOICE_MODE_NONE); }

    DetailFragment.java 在DetailFragment中添加全局变量ITEM_ID用于传递id参数

    public static String ITEM_ID="ITEM_ID";

    优化后显示效果如下 这样就实现了不同分辨率屏幕显示不同页面的功能 有机会再更新 菜鸡遁走…

    Processed: 0.093, SQL: 9