學(xué)習(xí)路徑:最基礎(chǔ)ListView到 MVVM+DataBinding+RecyclerView

第一階段:基礎(chǔ)ListView的使用(包含重用和ViewHolder)

  • 首先是兩個(gè)xml布局文件,由于極其簡(jiǎn)單勒极,所以不做解釋

    activity_main.xml

     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     
         <ListView
             android:id="@+id/list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             />
     
     </LinearLayout>
    

    listview_item.xml

     <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="horizontal">
     
         <TextView
             android:id="@+id/text1"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"/>
     
         <TextView
             android:id="@+id/text2"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"/>
     </LinearLayout>
    
  • 一般情況下,listView需要三個(gè)類例如

    • Fruit (用于封裝item中的各個(gè)信息的類)
    • FruitAdapter (用于綁定xml中控件和item資源信息)
    • MainActivity (綁定Adapter和listView)

①M(fèi)ainActivity

    public class MainActivity extends AppCompatActivity {
        //用于存放listView資源信息
        ArrayList<Fruit> arrayList = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ListView listView = findViewById(R.id.list_view);
            Fruit fruit1 = new Fruit("apple","abcde");
            Fruit fruit2 = new Fruit("banana","sadad");
            Fruit fruit3 = new Fruit("orange","fdgdf");
            arrayList.add(fruit1);
            arrayList.add(fruit2);
            arrayList.add(fruit3);
    
         //聲明并綁定Adapter和listView
            FruitAdapter adapter = new FruitAdapter(this,R.layout.listview_item,arrayList);
            listView.setAdapter(adapter);
    
         //點(diǎn)擊事件
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    Fruit fruit = arrayList.get(i);
                    Toast.makeText(MainActivity.this,fruit.getName()+"被點(diǎn)擊了",Toast.LENGTH_SHORT).show();
                }
            });
    
        }
    }

 ②FruitAdapter最重要的類T墼病s镎隆倒庵!
 
 - ViewHolder的使用結(jié)合了viewitem的回收機(jī)制耘纱,達(dá)到減少高消耗的findById的效果
 
 
        public class FruitAdapter extends ArrayAdapter {
    
            private int resourse;
        
            public FruitAdapter(Context context, int resource , List<Fruit> objects) {
                super(context, resource,objects);
                this.resourse = resource;
            }
        
            @Override
            public View getView(int position,  View convertView,ViewGroup parent) {
                Fruit fruit = (Fruit) getItem(position);
                ViewHolder viewHolder;
                if (convertView == null){
                    convertView = LayoutInflater.from(getContext()).inflate(resourse,null);
                    viewHolder = new ViewHolder();
                    viewHolder.text1 = convertView.findViewById(R.id.text1);
                    viewHolder.text2 = convertView.findViewById(R.id.text2);
                    convertView.setTag(viewHolder);
        
                }else {
                     viewHolder = (ViewHolder) convertView.getTag();
                }
        
                viewHolder.text1.setText(fruit.getName());
                viewHolder.text2.setText(fruit.getDiscription());
        
                return convertView;
            }
        
            class ViewHolder{
                TextView text1;
                TextView text2;
            }
        }

Fruit

    public class Fruit {
    
        private String name ;
        private String discription;
    
        public Fruit(String name,String discription){
            this.name = name;
            this.discription = discription;
        }
    
        public String getName() {
            return name;
        }
    
        public String getDiscription() {
            return discription;
        }
    }

第二階段:listView結(jié)合DataBinding的使用

  • 什么是DataBinding扣猫?

          早在2015谷歌 I/O大會(huì)上殴玛,就介紹了一個(gè)新的框架DataBinding钠惩,從名字就可以看出
      來(lái),這是一個(gè)數(shù)據(jù)綁定框架族阅。我們?yōu)槭裁匆褂肈ataBinding篓跛?
          1.再也不需要編寫(xiě)findViewById了,有人會(huì)說(shuō)坦刀,已經(jīng)有butterknife了愧沟,很好用。
          2.更新UI數(shù)據(jù)需切換至UI線程鲤遥,也有人說(shuō)沐寺,有rxjava了。
       但是DataBinding盖奈,不僅僅能解決這2個(gè)問(wèn)題混坞,它的核心優(yōu)勢(shì)在于,它解決了將數(shù)據(jù)分解映
       射到各個(gè)view的問(wèn)題。什么個(gè)意思究孕?具體來(lái)說(shuō)啥酱,就是針對(duì)每個(gè)Activity或者Fragment的
       布局,在編譯階段厨诸,會(huì)生成一個(gè)ViewDataBinding類的對(duì)象镶殷,該對(duì)象持有Activity要展
       示的數(shù)據(jù)和布局中的各個(gè)view的引用。同時(shí)還有如下優(yōu)勢(shì):將數(shù)據(jù)分解到各個(gè)view微酬、在UI
       線程上更新數(shù)據(jù)绘趋、監(jiān)控?cái)?shù)據(jù)的變化,實(shí)時(shí)更新颗管,這樣一來(lái)陷遮,你要展示的數(shù)據(jù)已經(jīng)和展示它的
       布局緊緊綁定在了一起。我認(rèn)為這才是DataBinding真正的魅力所在垦江。
    
  • 在build.gradle android模塊中添加如下配置

      android {
           dataBinding {
              enabled = true
           }
      }
    
  • 創(chuàng)建一個(gè)JavaBean對(duì)象

      public class UserBean {
    
          private String name;
      
          private int age;
      
          public UserBean(String name , int age){
              this.name = name;
              this.age = age;
          }
      
          public int getAge() {
              return age;
          }
      
          public String getName() {
              return name;
          }
      }
    
  • xml文件

     這里和以前使用的xml不同帽馋,根節(jié)點(diǎn)變成了layout,里面包括了data節(jié)點(diǎn)和傳統(tǒng)的布局疫粥。這里的data節(jié)點(diǎn)作用是連接 View 和 Modle 的橋梁茬斧。在這個(gè)data節(jié)點(diǎn)中聲明一個(gè)variable變量腰懂,那值就可以輕松傳到布局文件中來(lái)了梗逮。而且TextView中沒(méi)有給控件定義id,而是在text的時(shí)候用了@{ }的方法绣溜,在括號(hào)里面直接引用UserBean對(duì)象的屬性即可完成賦值慷彤。
    
    
          <layout xmlns:android="http://schemas.android.com/apk/res/android">
      
      
              <data>
                  <variable
                      name="user"
                      type="com.example.mvvmdeme.UserBean" />
              </data>
          
              <LinearLayout
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">
                  <TextView
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:text="@{user.name}"/>
                  <TextView
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:text="@{String.valueOf(user.age)}"/>
              </LinearLayout>
          
          </layout>
    
  • MainActivity

    這個(gè)activity很簡(jiǎn)潔,沒(méi)有了控件的初始化的findViewById或者butterknife的那一堆注解(這個(gè)butterknife是啥意思我暫時(shí)還不知道)怖喻,也沒(méi)有了TextView的setText(),也就2行代碼而已底哗。大家應(yīng)該已經(jīng)看見(jiàn)了,這里用DataBindingUtil.setContentView代替了setContentView锚沸,然后創(chuàng)建一個(gè) UserBean 對(duì)象跋选,通過(guò) binding.setUser(userBean) 與 variable 進(jìn)行綁定。注意:這個(gè)ActivityMainBinding 是如何生成的呢哗蜈?他是繼承ViewDataBinding前标,這個(gè)類的生成是有規(guī)則的,它是根據(jù)對(duì)應(yīng)的布局文件的名字生成的距潘,可以直接使用的炼列,比如:activity_main-->ActivityMainBinding 、fragment-->FragmentBinding即:第一個(gè)單詞首字母大寫(xiě)音比,第二個(gè)單詞首字母大寫(xiě)俭尖,最后都會(huì)拼上Binding就是生成的Binding類,

    <b>注意:在配置dataBinding = true之后生成的文件根節(jié)點(diǎn)才會(huì)是layout,所以之前生成的文件需要手動(dòng)修改,只有根節(jié)點(diǎn)為layout的xml文件系統(tǒng)才會(huì)生成對(duì)應(yīng)的Binding類稽犁。</b>

     public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //系統(tǒng)生成的類
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            UserBean bean = new UserBean("張三",25);
            binding.setUser(bean);
            //setContentView(R.layout.activity_main);
        }
    }

第三階段:RecyclerView的使用

RecyclerView是什么焰望?

從Android 5.0開(kāi)始,谷歌公司推出了一個(gè)用于大量數(shù)據(jù)展示的新控件RecylerView缭付,可以用來(lái)代替?zhèn)鹘y(tǒng)的ListView柿估,更加強(qiáng)大和靈活。RecyclerView的官方定義如下:

    A flexible view for providing a limited window into a large data set.

從定義可以看出陷猫,flexible(可擴(kuò)展性)是RecyclerView的特點(diǎn)秫舌。
RecyclerView是support-v7包中的新組件,是一個(gè)強(qiáng)大的滑動(dòng)組件绣檬,與經(jīng)典的ListView相比足陨,同樣擁有item回收復(fù)用的功能,這一點(diǎn)從它的名字Recyclerview即回收view也可以看出娇未。

一些簡(jiǎn)單使用

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
//設(shè)置布局管理器  
recyclerView.setLayoutManager(layoutManager);  
//設(shè)置為垂直布局墨缘,這也是默認(rèn)的  
layoutManager.setOrientation(OrientationHelper. VERTICAL);  
//設(shè)置Adapter  
recyclerView.setAdapter(recycleAdapter);  
 //設(shè)置分隔線  
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
//設(shè)置增加或刪除條目的動(dòng)畫(huà)  
recyclerView.setItemAnimator( new DefaultItemAnimator());  

簡(jiǎn)單使用

  • 在build.gradle文件中引入該類。

    在Android Gradle Plugin 3.0.0 以前零抬,你可以使用以下進(jìn)行配置

    compile 'com.android.support:recyclerview-v7:23.4.0'
    

    但是在3.0.0之后镊讼,棄用了compile,具體依賴配置項(xiàng)可以參閱官方文檔

    https://developer.android.com/studio/build/dependencies?utm_source=android-studio#dependency_configurations
    

    3.0.0之后的依賴配置

    implementation 'com.android.support:recyclerview-v7:28.0.0'
    
  • 布局

activity_main.xml

    <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/RecycleView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
           />
    
    </LinearLayout>

item_1.xml

    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

        <TextView
            android:id="@+id/text1"
            android:layout_width="0dp"
            android:layout_height="120dp"
            android:layout_weight="1"
            android:textAlignment="center"
            android:gravity="center"/>
    
        <TextView
            android:id="@+id/text2"
            android:layout_width="0dp"
            android:layout_height="120dp"
            android:layout_weight="1"
            android:textAlignment="center"
            android:gravity="center"/>
    
    </LinearLayout>
  • 同樣是來(lái)一個(gè)JavaBean文件

      public class Fruit {
          private String name;
          private String description;
      
          public Fruit(String name,String description){
              this.name = name;
              this.description = description;
          }
      
          public String getName() {
              return name;
          }
      
          public String getDescription() {
              return description;
          }
      }
    
  • 最重要的一個(gè)適配器類平夜,以RecycleviewAdapter為例

    適配器類需要繼承自RecyclerView.Adapter<VH>蝶棋,VH為你的ViewHolder的類名

      public class RecyclerviewAdapter extends RecyclerView.Adapter <RecyclerviewAdapter.ViewHolder> {
      
          List<Fruit> list;
      
          public RecyclerviewAdapter(List<Fruit> list){
              this.list = list;
          }
      
          @NonNull
          @Override
          public RecyclerviewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
              View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_1, viewGroup, false);
              return new ViewHolder(v);
          }
      
          @Override
          public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
              final int position = i;
              viewHolder.textView1.setText(list.get(position).getName());
              viewHolder.textView2.setText(list.get(position).getDescription());
      //        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
      //            @Override
      //            public void onClick(View view) {
      //                Log.d("com.", "onClick: "+list.get(position).getName());
      //            }
      //        });
      
          }
      
      
          @Override
          public int getItemCount() {
              return list.size();
          }
      
          public static class ViewHolder extends RecyclerView.ViewHolder{
      
              private TextView textView1;
              private TextView textView2;
      
              public ViewHolder(@NonNull View itemView) {
                  super(itemView);
                  textView1 = itemView.findViewById(R.id.text1);
                  textView2  = itemView.findViewById(R.id.text2);
              }
         }
      }
    
  • MainActivity

      public class MainActivity extends AppCompatActivity {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              RecyclerView recyclerView = findViewById(R.id.RecycleView);
              List<Fruit> list = initData();
              recyclerView.setLayoutManager(new LinearLayoutManager(this));
              recyclerView.setAdapter(new RecyclerviewAdapter(list));
              //添加分割線
              recyclerView.addItemDecoration(new DividerItemDecoration(this,1));
      
          }
      
          private List<Fruit> initData(){
      
              List<Fruit> list = new ArrayList<>();
      
              Fruit fruit1 = new Fruit("apple","first");
              Fruit fruit2 = new Fruit("orange","second");
              Fruit fruit3 = new Fruit("pear","third");
              Fruit fruit4 = new Fruit("banana","firth");
              Fruit fruit5 = new Fruit("saf","fifth");
              Fruit fruit6 = new Fruit("dsfsd","sixth");
              Fruit fruit7 = new Fruit("nbm","seventh");
      
              list.add(fruit1);
              list.add(fruit2);
              list.add(fruit3);
              list.add(fruit4);
              list.add(fruit5);
              list.add(fruit6);
              list.add(fruit7);
      
              return list;
          }
      }
    

其他更詳細(xì)可以參考博客,我覺(jué)得寫(xiě)的挺全的

 http://www.reibang.com/p/4f9591291365

階段四:在RecyclerView中使用DataBinding

本階段是在RecyclerView中使用DataBinding忽妒,所以兩者結(jié)合的時(shí)候玩裙,Adapter中方法略有不不同,個(gè)人理解是要將ViewHolder和DataBinding結(jié)合段直,所以方法有所差異

  • 整體結(jié)構(gòu)可以看成四項(xiàng)
    • Activity
    • Adapter
    • BaseAdapter(幫助我們有效管理Adapter)
    • JavaBean

先來(lái)看一下最重要的Adapter

基類(用于管理的父類):

    public abstract class BaseBindRecyclerViewAdapter<T> extends RecyclerView.Adapter {
    
        public List<Fruit> mList; //數(shù)據(jù)源
        
        //這個(gè)inflater我個(gè)人感覺(jué)是用于提供子類進(jìn)行xml綁定時(shí)需要的參數(shù)吃溅,所以在父類中統(tǒng)一實(shí)現(xiàn)聲明
        public LayoutInflater inflater;
    
        public BaseBindRecyclerViewAdapter(Context context, List<Fruit> mList) {
            this.mList = mList;
            inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
    
        @Override
        public int getItemCount() {
            return mList.size();
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return onCreateMyViewHolder(parent,viewType);
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            onBindMyViewHolder(holder, position);
        }
    
        //獲取Item布局
        public abstract RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType);
    
        //綁定數(shù)據(jù)
        public abstract void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position);
    }

再來(lái)看看具體實(shí)現(xiàn)的子類,此處以RecyclerviewAdapter.java為例

    public class RecyclerviewAdapter extends BaseBindRecyclerViewAdapter<RecyclerviewAdapter.ViewHolder> {
    
        //數(shù)據(jù)集
        List<Fruit> list;
    
        public RecyclerviewAdapter(Context context,List<Fruit> list){
            super(context,list);
            this.list = list;
        }
    
        //這個(gè)方法有所不同鸯檬,綁定xml的方式轉(zhuǎn)為用Binding進(jìn)行綁定决侈,然后把binding當(dāng)做參數(shù)用于返回一個(gè)ViewHolder
        @Override
        public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
            Item1Binding binding = DataBindingUtil.inflate(inflater,R.layout.item_1,parent,false);
            return new ViewHolder(binding);
        }
    
        //這個(gè)方法也不一樣,設(shè)置Bean的方式改為在binding中設(shè)置
        @Override
        public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {
            Fruit fruitBean =  list.get(position);
            ((ViewHolder) holder).getBinding().setFruit(fruitBean);
            ((ViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問(wèn)題
    
        }
    
            
        @Override
        public int getItemCount() {
            return list.size();
        }
    
        //ViewHolder類有不同喧务,不在進(jìn)行繁瑣的findById的綁定赖歌,綁定工作由DataBinding進(jìn)行,DataBinding作為ViewHolder的參數(shù)蹂楣。
        public static class ViewHolder extends RecyclerView.ViewHolder{
    
            private Item1Binding binding;
    
            public Item1Binding getBinding() {
                return binding;
            }
    
            public ViewHolder(Item1Binding binding) {
                super(binding.getRoot());
    
                this.binding = binding;
            }
        }
    }

MainActivity

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //改為DataBinding綁定xml的方式
        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);

        List<Fruit> list = initData();
        //以下設(shè)置項(xiàng)唯一差別就是不是直接在RecyclerView設(shè)置,而是轉(zhuǎn)為在binding下的RecycleView中設(shè)置俏站。
        binding.RecycleView.setLayoutManager(new LinearLayoutManager(this));
        binding.RecycleView.setAdapter(new RecyclerviewAdapter(this,list));
        binding.RecycleView.addItemDecoration(new DividerItemDecoration(this,1));

    }

xml以及Bean和以上DataBing代碼中一樣。

階段五:使用MVVM架構(gòu)+DataBinding實(shí)現(xiàn)RecyclerView

既然提到了MVVM就不得不總結(jié)一下android三個(gè)框架:MVC,MVP,MVVM

  • MVC

    • 視圖層(View)

      對(duì)應(yīng)于xml布局文件

    • 控制層(Controller)

      Android的控制層是由Activity來(lái)承擔(dān)的痊土,Activity本來(lái)主要是作為初始化頁(yè)面肄扎,展示數(shù)據(jù)的操作,但是因?yàn)閄ML視圖功能太弱,所以Activity既要負(fù)責(zé)視圖的顯示又要加入控制邏輯犯祠,承擔(dān)的功能過(guò)多旭等,在復(fù)雜一點(diǎn)的頁(yè)面Activity代碼量達(dá)到1000+也就不足為奇了。

    • 模型層(Model)

      我們針對(duì)業(yè)務(wù)模型衡载,建立的數(shù)據(jù)結(jié)構(gòu)和相關(guān)的類搔耕,它主要負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)庫(kù)處理痰娱,I/O的操作弃榨。

  • MVP

    在Android開(kāi)發(fā)中,Activity并不是一個(gè)標(biāo)準(zhǔn)的MVC模式中的Controller梨睁,本來(lái)它的首要職責(zé)是加載應(yīng)用的布局和初始化用戶界面鲸睛,接受并處理來(lái)自用戶的操作請(qǐng)求,進(jìn)而作出響應(yīng)坡贺。在MVC模式下隨著界面及其邏輯的復(fù)雜度不斷提升官辈,Activity類的職責(zé)不斷增加,以致變得龐大臃腫遍坟。

    • 視圖層(View)

      負(fù)責(zé)繪制UI元素拳亿、與用戶進(jìn)行交互,對(duì)應(yīng)于xml愿伴、Activity肺魁、Fragment、Adapter

    • 模型層(Model)

      負(fù)責(zé)存儲(chǔ)公般、檢索万搔、操縱數(shù)據(jù)胡桨,一般包含網(wǎng)絡(luò)請(qǐng)求官帘,數(shù)據(jù)庫(kù)處理,I/O流昧谊。

    • 控制層(Presenter)

      Presenter是整個(gè)MVP體系的控制中心刽虹,作為View與Model交互的中間紐帶,處理View于Model間的交互和業(yè)務(wù)邏輯呢诬。

    從去年到現(xiàn)在涌哲,MVP的設(shè)計(jì)思想在項(xiàng)目中用得比較多,它的具體實(shí)現(xiàn)就是接收到View的請(qǐng)求尚镰,從Model層獲取數(shù)據(jù)阀圾,將數(shù)據(jù)進(jìn)行處理,通過(guò)View層的接口回調(diào)給Activity或者Fragment狗唉。MVP能夠讓Activity成為真正的View初烘,只做UI相關(guān)的事。它的優(yōu)點(diǎn)還是很多的,不然也不會(huì)有這么多人喜歡它的肾筐,

  • 優(yōu)點(diǎn)如下:

    • 1哆料、模型與視圖完全分離,我們可以修改視圖而不影響模型吗铐;

    • 2东亦、項(xiàng)目代碼結(jié)構(gòu)(文件夾)清晰,一看就知道什么類干什么事情唬渗;

    • 3典阵、我們可以將一個(gè)Presenter用于多個(gè)視圖,而不需要改變Presenter的邏輯镊逝。這個(gè)特性非常的有用萄喳,因?yàn)橐晥D的變化總是比模型的變化頻繁

    • 4、協(xié)同工作(例如在設(shè)計(jì)師沒(méi)出圖之前可以先寫(xiě)一些業(yè)務(wù)邏輯代碼或者其他人接手代碼改起來(lái)比較容易)

  • 盡管這樣蹋半,MVP模式也有不足之處他巨,不然也不會(huì)推出MVVM了,缺點(diǎn)如下:

    • Presente層與View層是通過(guò)接口進(jìn)行交互的减江,View層可能會(huì)有大量的接口染突,因?yàn)橛锌赡芎脦讉€(gè)Activity都是去實(shí)現(xiàn)同一個(gè)View接口,那么所有用到的Activity都要去實(shí)現(xiàn)所有的方法(不管你是否用到)辈灼,而且如果后面有些方法要?jiǎng)h改份企,Presenter和Activity都要改動(dòng),比較麻煩巡莹;

    • MVP把Activity相當(dāng)?shù)囊徊糠重?zé)任放到了Presenter來(lái)處理司志,復(fù)雜的業(yè)務(wù)同時(shí)也可能會(huì)導(dǎo)致P層太大,一旦業(yè)務(wù)邏輯越來(lái)越多降宅,View定義的方法越來(lái)越多骂远,會(huì)造成Activity和Fragment實(shí)現(xiàn)的方法越來(lái)越多,依然臃腫腰根。

  • MVVM

    • Model :負(fù)責(zé)數(shù)據(jù)實(shí)現(xiàn)和邏輯處理激才,類似MVP。

    • View : 對(duì)應(yīng)于Activity和XML额嘿,負(fù)責(zé)View的繪制以及與用戶交互瘸恼,類似MVP。

    • ViewModel : 創(chuàng)建關(guān)聯(lián)册养,將model和view綁定起來(lái),如此之后东帅,我們model的更改,通過(guò) viewmodel反饋給view,從而自動(dòng)刷新界面球拦。

  • 對(duì)于各個(gè)架構(gòu)要更詳細(xì)了解可以參考博客:

       http://www.reibang.com/p/4830912f5162
    

    實(shí)戰(zhàn)

我自己又敲了一個(gè)思路清晰的簡(jiǎn)易版MVVM Demo,助于了解MVVM項(xiàng)目結(jié)構(gòu)靠闭。(以下言論基于自己理解)
首先MVVM項(xiàng)目分為幾個(gè)部分:(但是我分成了6個(gè)類比不代表只需要6個(gè)java就完事了邓夕!)

  • View
    Activity、xml阎毅、Fragment等等

  • Model

  • ViewModal

  • Adapter

  • implement

  • JavaBean

先來(lái)總結(jié)一下我在敲代碼過(guò)程中體會(huì)到的每個(gè)角色的作用

  • View-如MainActivity

    這部分主要是編寫(xiě)界面焚刚,可以進(jìn)行一些和數(shù)據(jù)無(wú)關(guān)的UI的修改和設(shè)計(jì)

    實(shí)例化Bing,綁定xml

    實(shí)例化Adapter

    實(shí)例化ViewModel(需要時(shí)把自身注入作為ViewModel的參數(shù))

    實(shí)現(xiàn)一些響應(yīng)或者操作扇调,具體實(shí)現(xiàn)調(diào)用ViewModel實(shí)現(xiàn)(所以需要聲明一個(gè)接口用來(lái)規(guī)范ViewModel)

  • Model (用于加載新數(shù)據(jù))- 繼承自一個(gè)Model的專用的接口來(lái)實(shí)現(xiàn)所需方法

  • ViewModel (綁定來(lái)自Model的數(shù)據(jù)和Adapter) - 繼承自一個(gè)ViewModel的專用的接口來(lái)實(shí)現(xiàn)所需方法

    這個(gè)VM參數(shù)相對(duì)來(lái)說(shuō)就比較多矿咕,需要在構(gòu)造時(shí)傳入Adapter用于數(shù)據(jù)更新,需要Model的實(shí)例化,在ViewModel實(shí)現(xiàn)的方法需要Model的參與狼钮,把自身作為響應(yīng)者注入Model碳柱,然后Model數(shù)據(jù)變化后調(diào)用響應(yīng)者的其他方法進(jìn)行實(shí)現(xiàn)。

  • Adapter

    Adapter的作用沒(méi)有大差異熬芜,還是用來(lái)管理itemView的綁定和數(shù)據(jù)綁定莲镣,提供給ViewModel用于相應(yīng)數(shù)據(jù)變化的接口。

上代碼

Bean代碼和從前一樣就不記錄了

  • MainActivity

       public class MainActivity extends AppCompatActivity implements View.OnClickListener {
       
           ViewModel viewModel ;
       
           @Override
           protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               //setContentView(R.layout.activity_main);
               ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
               //RecyclerView recyclerView = findViewById(R.id.RecycleView);
               List<Fruit> list = initData();
               RecyclerviewAdapter adapter =new RecyclerviewAdapter(this,list);
       
               viewModel = new ViewModel(this,list,adapter);
       
               binding.RecycleView.setLayoutManager(new LinearLayoutManager(this));
               binding.RecycleView.setAdapter(adapter);
               binding.RecycleView.addItemDecoration(new DividerItemDecoration(this,1));
       
           //O牙H鹞辍!這里不可以寫(xiě)成binding.btn.OnClickListener(this);寫(xiě)成這樣無(wú)效
               binding.setClick(this);
       
           }
       
       //其實(shí)這部分不應(yīng)該寫(xiě)在這里鼓拧,但是偷個(gè)懶半火,寫(xiě)一下初始數(shù)據(jù)
           private List<Fruit> initData(){
       
               List<Fruit> list = new ArrayList<>();
       
               Fruit fruit1 = new Fruit("apple","first");
               Fruit fruit2 = new Fruit("orange","second");
               Fruit fruit3 = new Fruit("pear","third");
               Fruit fruit4 = new Fruit("banana","firth");
               Fruit fruit5 = new Fruit("saf","fifth");
               Fruit fruit6 = new Fruit("dsfsd","sixth");
               Fruit fruit7 = new Fruit("nbm","seventh");
       
               list.add(fruit1);
               list.add(fruit2);
               list.add(fruit3);
               list.add(fruit4);
               list.add(fruit5);
               list.add(fruit6);
               list.add(fruit7);
       
               return list;
           }
       
           @Override
           public void onClick(View view) {
               switch (view.getId()){
                   case R.id.btn:
                       Log.d("com.example.mvvm", "onClick: 點(diǎn)擊了");
                       //通過(guò)ViewModel實(shí)現(xiàn)具體邏輯
                       viewModel.ToRefresh();
                   default:
                       Log.d("com.example.mvvm", "onClick: 點(diǎn)擊了");
       
       
               }
         }
       }
    
  • Adapter:

    BaseBindRecyclerViewAdapter

        public abstract class BaseBindRecyclerViewAdapter<T> extends RecyclerView.Adapter {
    
            public List<Fruit> mList; //數(shù)據(jù)源
            public LayoutInflater inflater;
        
            public BaseBindRecyclerViewAdapter(Context context, List<Fruit> mList) {
                this.mList = mList;
                
                //這個(gè)是為了子類綁定xml文件傳參使用,上面也提到了
                inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            }
        
            @Override
            public int getItemCount() {
                return mList.size();
            }
        
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return onCreateMyViewHolder(parent,viewType);
            }
        
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                onBindMyViewHolder(holder, position);
            }
        
            //獲取Item布局
            public abstract RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType);
        
            //綁定數(shù)據(jù)
            public abstract void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position);
        
           //這個(gè)方法就是用于相應(yīng)數(shù)據(jù)變化的,最好是把這個(gè)方法寫(xiě)進(jìn)接口里
            public void addData(List<Fruit> list){
                this.mList.addAll(list);
                //一定要調(diào)用這個(gè)方法不然數(shù)據(jù)不會(huì)更新
                notifyDataSetChanged();
            }
    
         }
    

    子類RecyclerviewAdapter

        public class RecyclerviewAdapter extends BaseBindRecyclerViewAdapter<RecyclerviewAdapter.ViewHolder> {
        
            List<Fruit> list;
        
            public RecyclerviewAdapter(Context context,List<Fruit> list){
                super(context,list);
                this.list = list;
            }
        
            @Override
            public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
                Item1Binding binding = DataBindingUtil.inflate(inflater,R.layout.item_1,parent,false);
                return new ViewHolder(binding);
            }
        
            @Override
            public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {
                Fruit fruitBean =  list.get(position);
                ((ViewHolder) holder).getBinding().setFruit(fruitBean);
                ((ViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問(wèn)題
        
            }
        
        //這方法暫時(shí)用不到
        //    @Override
        //    public int getItemViewType(int position) {
        //        return list.get(position).getItemViewType();
        //    }
        
            @Override
            public int getItemCount() {
                return list.size();
            }
        
            public static class ViewHolder extends RecyclerView.ViewHolder{
        
                private Item1Binding binding;
        
                public Item1Binding getBinding() {
                    return binding;
                }
        
                public ViewHolder(Item1Binding binding) {
                    super(binding.getRoot());
                    this.binding = binding;
                }
           }
        }
    
  • Model接口

       public interface ModelImp {
           public void NewData(VMImp listener);
       }
    
  • Model

        public class Model implements ModelImp {
    
            List<Fruit> list;
        
            public Model(List<Fruit> list){
                this.list = list;
            }
        
            //這個(gè)方式用于實(shí)現(xiàn)數(shù)據(jù)變化季俩,把實(shí)現(xiàn)了VMImp接口的VM作為響應(yīng)者注入钮糖,然后調(diào)用相應(yīng)者的方法
            @Override
            public void NewData(VMImp listener) {
                List<Fruit> list = new ArrayList<>();
        
                Fruit fruit1 = new Fruit("NEW","first");
                Fruit fruit2 = new Fruit("NEW","second");
                Fruit fruit3 = new Fruit("NEW","third");
                Fruit fruit4 = new Fruit("NEW","firth");
                Fruit fruit5 = new Fruit("NEW","fifth");
                Fruit fruit6 = new Fruit("NEW","sixth");
                Fruit fruit7 = new Fruit("NEW","seventh");
        
                list.add(fruit1);
                list.add(fruit2);
                list.add(fruit3);
                list.add(fruit4);
                list.add(fruit5);
                list.add(fruit6);
                list.add(fruit7);
        
                listener.succeed(list);
        
            }
        }
    
  • ViewModel的接口

        public interface VMImp {
            //MainActivity調(diào)用
            public void ToRefresh();
            //用于Model回調(diào)
            public void succeed(List<Fruit> list);
        }
    
  • ViewModel

       public class ViewModel implements VMImp{
           private AppCompatActivity view;
           private Model model;
           private RecyclerviewAdapter adapter;
       
       
           public ViewModel(AppCompatActivity view, List<Fruit> list,RecyclerviewAdapter adapter){
               model = new Model(list);
               this.view = view;
               this.adapter = adapter;
           }
       
           //MainActivity調(diào)用
         @Override
           public void ToRefresh() {
               model.NewData(this);
       
           }
       
           //用于Model回調(diào)
           public void succeed(List<Fruit> list){
               adapter.addData(list);
           }
       }
    
  • activity_main.xml

       <layout xmlns:android="http://schemas.android.com/apk/res/android">
           <data>
                   <variable
                       name="click"
                       type="android.view.View.OnClickListener" />
           </data>
           <LinearLayout
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:orientation="vertical">
                   <Button
                       android:id="@+id/btn"
                       android:layout_width="match_parent"
                       android:layout_height="100dp"
                       android:text="更新"
                       android:onClick="@{click}"/>
                   <android.support.v7.widget.RecyclerView
                       android:id="@+id/RecycleView"
                       android:layout_width="match_parent"
                       android:layout_height="match_parent"
                       />
           </LinearLayout>
       </layout>
    
  • item_1.xml

         <layout  xmlns:android="http://schemas.android.com/apk/res/android">
             <data>
                 <variable
                     name="fruit"
                     type="com.example.recyclerviewwithdatabinding.Fruit" />
             </data>
         
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="horizontal">
         
                 <TextView
                     android:layout_width="0dp"
                     android:layout_height="120dp"
                     android:layout_weight="1"
                     android:textAlignment="center"
                     android:gravity="center"
                     android:text="@{fruit.name}"/>
         
                 <TextView
                     android:layout_width="0dp"
                     android:layout_height="120dp"
                     android:layout_weight="1"
                     android:textAlignment="center"
                     android:gravity="center"
                     android:text="@{fruit.description}"/>
         
             </LinearLayout>
         
         </layout>
    

可參考文檔

    http://www.reibang.com/p/4f9591291365
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酌住,隨后出現(xiàn)的幾起案子店归,更是在濱河造成了極大的恐慌,老刑警劉巖酪我,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件消痛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡祭示,警方通過(guò)查閱死者的電腦和手機(jī)肄满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)谴古,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)质涛,“玉大人,你說(shuō)我怎么就攤上這事掰担』懵剑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵带饱,是天一觀的道長(zhǎng)毡代。 經(jīng)常有香客問(wèn)我阅羹,道長(zhǎng),這世上最難降的妖魔是什么教寂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任捏鱼,我火速辦了婚禮,結(jié)果婚禮上酪耕,老公的妹妹穿的比我還像新娘导梆。我一直安慰自己,他們只是感情好迂烁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布看尼。 她就那樣靜靜地躺著,像睡著了一般盟步。 火紅的嫁衣襯著肌膚如雪藏斩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天却盘,我揣著相機(jī)與錄音狰域,去河邊找鬼。 笑死黄橘,一個(gè)胖子當(dāng)著我的面吹牛北专,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旬陡,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拓颓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了描孟?” 一聲冷哼從身側(cè)響起驶睦,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匿醒,沒(méi)想到半個(gè)月后场航,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廉羔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年溉痢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憋他。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孩饼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竹挡,到底是詐尸還是另有隱情镀娶,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布揪罕,位于F島的核電站梯码,受9級(jí)特大地震影響宝泵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轩娶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一儿奶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鳄抒,春花似錦廓握、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至闹司,卻和暖如春娱仔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背游桩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工牲迫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人借卧。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓盹憎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铐刘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陪每,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容