Fragmentの代替品を探す試み

20418 ワード

Androidが3.0でFragmentを発売してから、今では多くのアプリケーションの設計がFragmentに基づいて構築されています.複数のtabがこのようなニーズを切り替えるように、Fragmentを使用することができます.また、Fragmentは一連のライフサイクルのコールバックを提供し、データの保存やリカバリなど、多くの特殊なニーズを実現するのに役立ちます.
Fragment自体の登場は、タブレットのマルチスクリーンインタフェースの展示問題を解決するためであり、タブレットは携帯電話よりも多くのコンテンツを展示することができるため、Fragmentを使用して異なるサイズに応じて異なるコンテンツを展示する需要を実現することができ、この異なるコンテンツはより大きなサイズでより多くのコンテンツを表示することを意味することが多い.
人々の実際の符号化作業につれて、Fragmentを使用すると、1つのActivityが複数のFragmentを管理できるため、Fragmentを1つのインタフェースとすれば、複数のインタフェースの切り替えを実現することができ、このような作業は以前の布局ファイルでの制御よりも管理され、レイアウトが多重化されることが分かった.Activityの役割はFragmentの管理コンテナにすぎず、FragmentがActivityと同期したライフサイクルを持っているため、多くの業務がFragmentに置かれています.
現在、多くのインタフェースの開発作業はActivityに複数のFragmentを加えた設計モデルを使用しているが、これは良い方法であるが、Fragmentという利器を完全に把握するには、理解しなければならない仕事が非常に多く、Fragment間のパラメータ伝達、Fragment間の切り替えと状態の保存など、Fragmentに関する多くの話題を展開することができる.これらはかなりの範囲であり、グーグルもFragmentの使用の見通しを見ており、DialogFrament、ListFragmentなどの開発者の使用を容易にし、FragmentとActivityの間のライフサイクルの関係をカプセル化し、Activityのライフサイクルを傍受していくつかの機能を実現することができ、終了時に現在の非同期タスクを停止するなどのニーズを実現することができます.
Fragmentのコードをよく見ると、Activityのレイアウトで指定された場所に対応するレイアウトを追加し、さまざまなライフサイクルのコールバックを実現するためにリスニングをバインドしていることにほかならないことがわかります.
Androidソースコードをシミュレートして、自分でFragmentの代替品を作ることもできます.
我々の今回の試みはFragmentインタフェースの多重化機能を実現することであり,これは最も一般的なシーンである.
FragmentManagerに対応して、私達はViewHolderManagerでViewを管理して、Fragmentに対応して、PartViewHolderを使います.
私たちがViewを置き換えるときに対応するViewをより速く見つけるためには、FragmentManagerがFragmentを検索しているように、key値は私たちが指定したidであり、valueは対応するPartView HolderであるHashMapが必要です.
PartView Holderは抽象的なものにすぎず、Viewの占有を表し、より正確にはViewの制御クラスであるため、いくつかの共通の行為を持っており、実現上は抽象的なクラスである.
Javaでは、抽象のセットを定義するには、抽象クラスとインタフェースの2つがあります.この2つはマルチステートの表現です.抽象クラスは「is a」の関係を表し,インタフェースは「has a」の関係を表す.いくつかのコードを見たことがありますが、Duckのような名前は、意外にも1つのインタフェースにすぎません.インタフェースは行為プロトコルのセットを表しています.flyableのようなインタフェース名です.抽象的な名前が名詞であれば、抽象的なクラスであるかどうかを考えることができます.形容詞や動詞であれば、インタフェースであるかどうかを考えることができます.
PartView Holderの実現は以下の通りである.
 1 public abstract class PartViewHolder {
 2 
 3    protected View rootView;
 4 
 5    protected LayoutInflater inflater;
 6 
 7    public PartViewHolder(LayoutInflater inflater) {
 8 
 9       this.inflater = inflater;
10 
11    }
12 
13    protected abstract View getRootView();
14 
15    protected abstract void bindViews();
16 
17    public abstract void resetView();
18 
19 }

ここではActivityのContextは転送されていません.多くの人がViewに関するカスタマイズを実現する際に、便利のためにContextに転送します.LayoutInflaterはContextで取得する必要があるからですが、レイアウトでレンダリングするためでなければContextを転送しなくてもいいです.これもメモリの漏洩を防ぐための方法です.
転送されたrootViewは、Viewが追加するrootを提供するために使用され、bindViewはコントロールを初期化するために使用され、resetViewはViewのクリーンアップを提供します.
PartView Holderのサブクラスを実現します.
 1 public class LoginViewHolder extends PartViewHolder {
 2     private LinearLayout llLogin;
 3     private LinearLayout llRegister;
 4     public LoginViewHolder(LayoutInflater inflater) {
 5         super(inflater);
 6         rootView = inflater.inflate(R.layout.view_login_register, null);
 7         rootView.setTag(false);
 8     }
 9     @Override
10     protected View getRootView() {
11         return rootView;
12     }
13     @Override
14     protected void bindViews() {
15         llLogin = (LinearLayout) rootView.findViewById(R.id.ll_login);
16         llRegister = (LinearLayout) rootView.findViewById(R.id.ll_register);
17         llLogin.setOnClickListener(new View.OnClickListener() {
18             @Override
19             public void onClick(View v) {
20                ...
21             }
22         });
23         llRegister.setOnClickListener(new View.OnClickListener() {
24             @Override
25             public void onClick(View v) {
26                ...
27             }
28         });
29     }
30     @Override
31     public void resetView() {
32     }
33 }

上で実現したのはログインしたページです.
コンストラクタでrootViewを指定し、getRootViewを介してrootViewを返します.これにより、上位ビジネスがrootViewを操作し、bindViewがコンポーネントの初期化とイベントの傍受を行うことができます.
PartView Holderは、Viewの初期化と対応する操作を担当するために使用されます.
最も重要なクラス:ViewHolderManagerを見てみましょう.
ViewHolderManagerの実装は次のとおりです.
 1 public class ViewHolderManager {
 2     private static Map<String, PartViewHolder> viewMap;
 3     private static Map<String, PartInnerViewHolder> innerViewMap;
 4     public ViewHolderManager() {
 5         if(viewMap == null) {
 6             viewMap = new HashMap<>();
 7         }
 8         if(innerViewMap == null) {
 9             innerViewMap = new HashMap<>();
10         }
11     }
12     public void add(String key, PartViewHolder viewHolder) {
13         viewMap.put(key, viewHolder);
14     }
15     public void addInner(String innerKey, PartInnerViewHolder viewHolder) {
16         innerViewMap.put(innerKey, viewHolder);
17     }
18     public void showView(ViewGroup viewGroup, String key) throws Exception {
19         if (viewMap.size() == 0) {
20             throw new Exception("You have not add any views");
21         }
22         if (viewGroup == null) {
23             throw new Exception("not rootView");
24         }
25         if (viewGroup.getChildCount() > 0) {
26             viewGroup.removeAllViews();
27         }
28         PartViewHolder viewHolder = viewMap.get(key);
29         viewGroup.addView(viewHolder.getRootView());
30         if (!(boolean) viewHolder.getRootView().getTag()) {
31             viewHolder.bindViews();
32         }
33     }
34     public void showInnerView(String innerKey) throws Exception {
35         if (viewMap.size() == 0) {
36             throw new Exception("You have not add any views");
37         }
38         PartInnerViewHolder viewHolder = innerViewMap.get(innerKey);
39         ViewGroup viewGroup = (ViewGroup) viewHolder.getRootView();
40         if (viewGroup.getChildCount() > 0) {
41             viewGroup.removeAllViews();
42         }
43         viewGroup.addView(viewHolder.getInnerRootView());
44         if (!(boolean) viewHolder.getInnerRootView().getTag()) {
45             viewHolder.bindInnerViews();
46         }
47     }
48     public void resetView(String key) {
49         if (viewMap.containsKey(key)) {
50             PartViewHolder viewHolder = viewMap.get(key);
51             viewHolder.resetView();
52         }
53         if (innerViewMap.containsKey(key)) {
54             PartInnerViewHolder viewHolder = innerViewMap.get(key);
55             viewHolder.resetView();
56         }
57     }
58 }

ViewHolderManagerはHashMapのメンテナンスを担当しています.このHashMapには様々なPartViewHolderが保存されています.FragmentManagerと同様に、addで対応するPartView Holderを追加し、showViewを呼び出すときにidで表示するPartView Holderを指定します.
私たちのやり方は簡単です.指定したView GroupにrootViewを追加し、必要なViewをレイアウトに表示する目的に達します.追加する前に、追加したかどうかを判断し、あればremoveを削除します.
ここでは、Fragment自体にもFragmentを追加できる場合に対応するPartInnerView Holderも追加しました.
 1 public abstract class PartInnerViewHolder{
 2     protected View rootView;
 3     protected View innerRootView;
 4     protected LayoutInflater inflater;
 5     public PartInnerViewHolder(LayoutInflater inflater, View rootView){
 6         this.inflater = inflater;
 7         this.rootView = rootView;
 8     }
 9     protected abstract View getInnerRootView();
10     protected abstract void bindInnerViews();
11     protected abstract View getRootView();
12     protected abstract void resetView();
13 }

PartViewHolderに比べて、PartInnerViewHolderは、Fragment自体のレイアウトを指定するためにinnerRootViewを追加したにすぎません.
今になって、Fragmentの簡単な模倣品が完成しました.もちろん、私たちはどうして自分で車輪を作るのですか.ネストされたFragmentを使用する場合、多くの問題が発生します.直感的には、APPがバックグラウンドに置かれた後、メモリが不足している場合、Fragmentが重なり、他にも様々な状況があるので、最も簡略化されたFragmentをどのように真似するかを試みました.