zoukankan      html  css  js  c++  java
  • android 数据绑定(1)Ativity、Fragment、Item绑定数据源

    1.简介

    官方文档:

       https://developer.android.com/topic/libraries/data-binding

    官方示例:

      https://github.com/android/databinding-samples

    作用:

      把layout 与 数据对象关联,将layout上的组件与对象的成员绑定.让layout上的组件的值来自数据对象。如:

        <TextView android:text="@{data.value}" />

    2.简单用法:绑定Activity与数据源

    2.1 打开绑定选项

      只能在主module的 build.gradle 中打开,即使主module代码没有使用数据绑定,而他依赖的module使用了数据绑定,也要在主module中打开。

    1     buildFeatures{
    2         dataBinding true
    3         // for view binding :
    4         // viewBinding true
    5     }

    2.2 布局 activity_main.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <layout xmlns:android="http://schemas.android.com/apk/res/android">
     3     <data>
     4         <variable name="data" type="com.example.databind.Data" />      <!--声明的对象data-->
     5         <variable name="click" type="com.example.databind.Click" />
     6     </data>
     7     <androidx.constraintlayout.widget.ConstraintLayout >
     8 
     9         <TextView android:text="@{data.value, default = value}" />      <!--值来自数据对象data-->
    10         <Button android:onClick="@{() -> click.onStartClick(data)}" />
    11 
    12     </androidx.constraintlayout.widget.ConstraintLayout>
    13 </layout>

    其中

    • 跟元素<layout> 包含 元素 <data>和 跟布局<ConstraintLayout >
    • <data>元素用来定义layout使用的数据源,可以使用稍微复杂语法,如<import ...>,见第4、5行。
    • layout上的组件使用@{数据源名.成员}指定值,@{}内可以使用简单的的绑定表达式,如第10行。

    2.3 数据对象 Data.java

     1 package com.example.databind;
     2 
     3 public class Data {
     4     public String   key;
     5     public int      value;
     6 
     7     public Data(String key, int value){
     8         this.key = key;
     9         this.value = value;
    10     }
    11 
    12     @Override
    13     public String toString() {
    14         return "key = " + key + " value = " + value;
    15     }
    16 }

    2.4 建立绑定关系

     1 package com.example.databind;
     2 import android.os.Bundle;
     3 import androidx.appcompat.app.AppCompatActivity;
     4 import androidx.databinding.DataBindingUtil;
     5 
     6 import com.example.databind.databinding.ActivityMainBinding;
     7 
     8 public class MainActivity extends AppCompatActivity {
     9 
    10     private ActivityMainBinding binding;
    11     private Data                data;
    12 
    13     private void init(){
    14         data = new Data("time", 10);
    15         binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);
    16         binding.setData(data);
    17         binding.setClick(new Click(binding));
    18     }
    19 
    20     @Override
    21     protected void onCreate(Bundle savedInstanceState) {
    22         super.onCreate(savedInstanceState);
    23         setContentView(R.layout.activity_main);
    24         init();
    25     }
    26 }

     关于其中的 ActivityMainBinding :

    • 第15行得到了把一个activity和一个布局关联,得到一个绑定对象 ActivityMainBinding,它是绑定框架生成的,见5
    • 写完layout后 Rebuild Project 一下 就可以生成它。

    3. 绑定Fragment与数据源 

    常用的方法:

    • DataBindingUtil.inflate(inflater, R.layout.daily_data, container, false) 
    • DataBindingUtil.bind(root) ,返回可能为空的绑定对象。
     1     lateinit var binding : DailyDataBinding
     2 
     3     override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
     4         ): View? {
     5         binding = DataBindingUtil.inflate(inflater, R.layout.daily_data, container, false)
     6 
     7         binding.dailyShare.setOnClickListener(this::onShareClicked)
     8         //...   
     9         return binding.root
    10     }

      或者

    1     var binding : DailyBinding? = null
    2     fun initBinding(root : View){
    3         binding = DataBindingUtil.bind(root)
    4         binding?.page = page
    5         binding?.start = startTime
    6         //...          
    7     }

    3.1 布局文件

      1 <?xml version="1.0" encoding="utf-8"?>
      2 <layout xmlns:app="http://schemas.android.com/apk/res-auto"
      3     xmlns:android="http://schemas.android.com/apk/res/android">
      4     <data >
      5         <variable name="data" type="com.example.databind.Data" />
      6         <variable name="click" type="com.example.databind.Click" />
      7     </data>
      8 
      9     <androidx.constraintlayout.widget.ConstraintLayout
     10         android:layout_width="match_parent"
     11         android:layout_height="match_parent"
     12         android:clickable="true"
     13         >
     14         <TextView
     15             android:id="@+id/title"
     16             android:layout_width="wrap_content"
     17             android:layout_height="wrap_content"
     18             android:layout_marginTop="16dp"
     19             android:text="Hello Data Binding !"
     20             app:layout_constraintLeft_toLeftOf="parent"
     21             app:layout_constraintRight_toRightOf="parent"
     22             app:layout_constraintTop_toTopOf="parent" />
     23 
     24         <TextView
     25             android:id="@+id/key"
     26             android:layout_width="wrap_content"
     27             android:layout_height="wrap_content"
     28             android:layout_marginTop="16dp"
     29             android:text="@{data.key, default = key}"
     30             app:layout_constraintStart_toStartOf="parent"
     31             app:layout_constraintEnd_toStartOf="@+id/value"
     32             app:layout_constraintTop_toBottomOf="@+id/title"
     33             app:layout_constraintHorizontal_chainStyle="packed"
     34             />
     35 
     36         <TextView
     37             android:id="@+id/value"
     38             android:layout_width="wrap_content"
     39             android:layout_height="wrap_content"
     40             android:layout_marginStart="16dp"
     41             android:layout_marginLeft="16dp"
     42             android:text="@{String.valueOf(data.value),default = value}"
     43             app:layout_constraintEnd_toEndOf="parent"
     44             app:layout_constraintStart_toEndOf="@+id/key"
     45             app:layout_constraintTop_toTopOf="@+id/key" />
     46 
     47         <Button
     48             android:id="@+id/start"
     49             android:layout_width="wrap_content"
     50             android:layout_height="wrap_content"
     51             android:layout_marginTop="16dp"
     52             android:onClick="@{() -> click.onStartClick(data)}"
     53             android:text="start"
     54             android:textAllCaps="false"
     55             app:layout_constraintEnd_toStartOf="@+id/stop"
     56             app:layout_constraintHorizontal_chainStyle="packed"
     57             app:layout_constraintStart_toStartOf="parent"
     58             app:layout_constraintTop_toBottomOf="@+id/key" />
     59 
     60         <Button
     61             android:id="@+id/stop"
     62             android:layout_width="wrap_content"
     63             android:layout_height="wrap_content"
     64             android:layout_marginStart="16dp"
     65             android:layout_marginLeft="16dp"
     66             android:onClick="@{() -> click.onStopClicked(data)}"
     67             android:text="stop"
     68             android:textAllCaps="false"
     69             app:layout_constraintBottom_toBottomOf="@+id/start"
     70             app:layout_constraintEnd_toStartOf="@+id/reset"
     71             app:layout_constraintStart_toEndOf="@+id/start"
     72             app:layout_constraintTop_toTopOf="@+id/start" />
     73 
     74         <Button
     75             android:id="@+id/reset"
     76             android:layout_width="wrap_content"
     77             android:layout_height="wrap_content"
     78             android:layout_marginStart="16dp"
     79             android:layout_marginLeft="16dp"
     80             android:text="reset"
     81             android:textAllCaps="false"
     82             app:layout_constraintBottom_toBottomOf="@+id/start"
     83             app:layout_constraintEnd_toEndOf="parent"
     84             app:layout_constraintStart_toEndOf="@+id/stop"
     85             app:layout_constraintTop_toTopOf="@+id/start"
     86             app:layout_constraintVertical_bias="1.0" />
     87 
     88         <Button
     89             android:id="@+id/list_fragment"
     90             android:layout_width="wrap_content"
     91             android:layout_height="wrap_content"
     92             android:layout_marginTop="32dp"
     93             android:text="list fragment"
     94             android:textAllCaps="false"
     95             app:layout_constraintEnd_toEndOf="parent"
     96             app:layout_constraintStart_toStartOf="parent"
     97             app:layout_constraintTop_toBottomOf="@+id/stop" />
     98 
     99         <EditText
    100             android:id="@+id/editText"
    101             android:layout_width="wrap_content"
    102             android:layout_height="wrap_content"
    103             android:layout_marginTop="32dp"
    104             android:ems="10"
    105             android:inputType="textPersonName"
    106             android:text="Name"
    107             app:layout_constraintEnd_toEndOf="@+id/list_fragment"
    108             app:layout_constraintStart_toStartOf="@+id/list_fragment"
    109             app:layout_constraintTop_toBottomOf="@+id/list_fragment" />
    110 
    111     </androidx.constraintlayout.widget.ConstraintLayout>
    112 
    113 </layout>

    3.2 数据类

    Data.java

     1 package com.example.databind;
     2 
     3 public class Data {
     4     public String   key;
     5     public int      value;
     6 
     7     public Data(String key, int value){
     8         this.key = key;
     9         this.value = value;
    10     }
    11 
    12     @Override
    13     public String toString() {
    14         return "key = " + key + " value = " + value;
    15     }
    16 }

    click.java

     1 package com.example.databind;
     2 import android.util.Log;
     3 import androidx.databinding.ViewDataBinding;
     4 import java.util.Timer;
     5 import java.util.TimerTask;
     6 
     7 public class Click {
     8     ViewDataBinding     binding;
     9     private Timer       timer;
    10 
    11     public void onStartClick(final Data data){
    12         timer = new Timer();
    13         timer.schedule(new TimerTask() {
    14             @Override
    15             public void run() {
    16                 if (--data.value  < 0){
    17                     cancel();
    18                     return;
    19                 }
    20                 Log.e("ActivityMainBinding","data : " + data);
    21                 data.key = "left";
    22 //                binding.setData(data);
    23 //                binding.notifyChange();
    24 //                binding.notifyPropertyChanged(R.id.value);
    25 //                binding.value.invalidate();
    26                 if (!binding.hasPendingBindings()){
    27                     binding.invalidateAll();
    28                 }
    29             }
    30         },1000 * 1,1000 * 1);
    31     }
    32     public void onStopClicked(Data data){
    33         cancel();
    34     }
    35     private void cancel(){
    36         if (timer != null){
    37             timer.cancel();
    38             timer = null;
    39         }
    40     }
    41     public Click(ViewDataBinding binding){
    42         this.binding = binding;
    43     }
    44 }

    3.3 绑定

     1 package com.example.databind;
     2 
     3 import android.os.Bundle;
     4 import android.view.LayoutInflater;
     5 import android.view.View;
     6 import android.view.ViewGroup;
     7 
     8 import androidx.annotation.NonNull;
     9 import androidx.annotation.Nullable;
    10 import androidx.databinding.DataBindingUtil;
    11 import androidx.databinding.ViewDataBinding;
    12 import androidx.fragment.app.Fragment;
    13 
    14 import butterknife.ButterKnife;
    15 import butterknife.OnClick;
    16 
    17 public class MainFrgmt extends Fragment {
    18 
    19     ViewDataBinding             binding;
    20     private Data                data;
    21 
    22     private void init(){
    23         data = new Data("time", 10);
    24         binding.setVariable(BR.data,data);
    25         binding.setVariable(BR.click,new Click(binding));
    26     }
    27     @Nullable
    28     @Override
    29     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    30         binding = DataBindingUtil.inflate(inflater,R.layout.frgmt_main,container,false);
    31         View view = binding.getRoot();
    32         ButterKnife.bind(this,view);
    33         init();
    34         return view;
    35     }
    36 
    37     @OnClick(R.id.reset)
    38     public void onResetClicked(View view){
    39         data.key = "time";
    40         data.value = 10;
    41         binding.invalidateAll();
    42     }
    43 }

     其中:

    • 第31行创建绑定对象 binding
    • 第24、25行设置绑定的数据对象,其中的BR是绑定库生成的一个类,其中包含<data>中声明的变量的id。如data的id就是BR.data
    • 第41行更新数据对象

    4.Item绑定数据源

    4.1 item布局 

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <layout xmlns:app="http://schemas.android.com/apk/res-auto"
     3     xmlns:android="http://schemas.android.com/apk/res/android">
     4 
     5     <data>
     6         <variable name="item" type="com.example.databind.list.ItemData" />
     7     </data>
     8 
     9     <LinearLayout
    10         android:orientation="horizontal"
    11         android:layout_width="match_parent"
    12         android:layout_height="wrap_content"
    13         >
    14         <ImageView
    15             android:id="@+id/item_icon"
    16             android:layout_width="wrap_content"
    17             android:layout_height="wrap_content"
    18             android:layout_marginLeft="16dp"
    19             android:layout_marginStart="16dp"
    20             android:layout_marginTop="8dp"
    21             android:layout_marginBottom="8dp"
    22             android:layout_gravity="center_vertical"
    23             android:layout_weight="1"
    24             app:imageSrc="@{item.icon}" />
    25         <TextView
    26             android:id="@+id/item_title"
    27             android:layout_width="wrap_content"
    28             android:layout_height="wrap_content"
    29             android:layout_weight="5"
    30             android:layout_marginLeft="32dp"
    31             android:layout_marginStart="32dp"
    32             android:layout_marginRight="8dp"
    33             android:layout_marginEnd="8dp"
    34             android:layout_gravity="center_vertical"
    35             android:gravity="left|center_vertical"
    36             android:textAllCaps="false"
    37             android:textSize="16sp"
    38             android:layout_marginTop="8dp"
    39             android:layout_marginBottom="8dp"
    40             android:text="@{item.title,default = item}" />
    41     </LinearLayout>
    42 
    43 </layout>

    4.2 数据类

    1 package com.example.databind.list;
    2 
    3 public class ItemData {
    4     public String   title;
    5     public int      icon;
    6 }

    4.3 adapter

     1 package com.example.databind.list;
     2 
     3 import android.view.LayoutInflater;
     4 import android.view.View;
     5 import android.view.ViewGroup;
     6 import android.widget.ImageView;
     7 
     8 import androidx.annotation.NonNull;
     9 import androidx.databinding.BindingAdapter;
    10 import androidx.databinding.DataBindingUtil;
    11 import androidx.recyclerview.widget.RecyclerView;
    12 
    13 import com.example.databind.R;
    14 import com.example.databind.databinding.ListItemBinding;
    15 
    16 import java.util.ArrayList;
    17 import java.util.List;
    18 
    19 public class ListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    20 
    21     private List<ItemData>  datas = new ArrayList<>();
    22 
    23     public void initData(){
    24         for (int i = 0 ; i < 32; ++i){
    25             ItemData item = new ItemData();
    26             item.title = "item" + i;
    27             switch (i){
    28                 case 11: case 21:    case 31:
    29                 case 1 : item.icon = R.mipmap.icon1;    break;
    30 
    31                 case 12: case 22:    case 32:
    32                 case 2 : item.icon = R.mipmap.icon2;    break;
    33 
    34                 case 13: case 23:    case 33:
    35                 case 3 : item.icon = R.mipmap.icon3;    break;
    36 
    37                 case 14: case 24:    case 34:
    38                 case 4 : item.icon = R.mipmap.icon4;    break;
    39 
    40                 case 15: case 25:    case 35:
    41                 case 5 : item.icon = R.mipmap.icon5;    break;
    42 
    43                 case 16: case 26:
    44                 case 6 : item.icon = R.mipmap.icon6;    break;
    45 
    46                 case 17: case 27:
    47                 case 7 : item.icon = R.mipmap.icon7;    break;
    48 
    49                 case 18: case 28:
    50                 case 8 : item.icon = R.mipmap.icon8;    break;
    51 
    52                 case 19: case 29:
    53                 case 9 : item.icon = R.mipmap.icon9;    break;
    54                 default: item.icon = R.mipmap.icon0;    break;
    55             }
    56             datas.add(item);
    57         }
    58     }
    59     
    60     @BindingAdapter({"imageSrc"})
    61     public static void setImage(ImageView view, int icon){
    62         view.setImageResource(icon);
    63     }
    64 
    65     @NonNull
    66     @Override
    67     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    68         LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    69         View view = inflater.inflate(R.layout.list_item,parent,false);
    70         BindHolder holder = new BindHolder(view);
    71 
    72         return holder;
    73     }
    74 
    75     @Override
    76     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    77         BindHolder bh = (BindHolder) holder;
    78         ItemData item = datas.get(position);
    79         ListItemBinding binding = DataBindingUtil.bind(bh.itemView);
    80         binding.setItem(item);
    81     }
    82 
    83     @Override
    84     public int getItemCount() {
    85         return datas.size();
    86     }
    87 
    88 
    89     public static class BindHolder extends RecyclerView.ViewHolder{
    90 
    91         public BindHolder(@NonNull View itemView) {
    92             super(itemView);
    93         }
    94     }
    95 }

    5.ActivityMainBinding 等绑定类哪来的?

    5.1 他们是绑定框架生成的接口

    位置:

     以 Frgmt1Binding 为例,它的实现在 : appuildgeneratedap_generated_sourcesdebugoutcomexampledatabinddatabinding 目录下,

      comexampledatabind  是包名,如下:

    默认的命名规则:

      布局文件名按Pascal命名方法(忽略下划线_) + Binding , 如布局文件是 Frgmt1.xml 那么生成的类就是 Frgmt1Binding

    5.4 修改生成的绑定类的名字

     1 <layout xmlns:app="http://schemas.android.com/apk/res-auto"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     xmlns:android="http://schemas.android.com/apk/res/android">
     4     <data class="AAAAAAA">
     5         <import type="android.view.View" />
     6         <import type="com.example.databind.Exts" />
     7     </data>
     8     <androidx.constraintlayout.widget.ConstraintLayout
     9         android:clickable="true"
    10         android:background="#ffffff"
    11         android:layout_width="match_parent"
    12         android:layout_height="match_parent">
    13 ...

      这个示例 在当前模块的 databinding 包中生成绑定类  AAAAAAA 

    1     <data class=".BBBBBB">
    2 3     </data>

      这个示例 在模块包中生成绑定类  BBBBBB

    1     <data class="com.example.CCC">
    2 3     </data>

      这个 示例在 com.example 包中创建绑定类 CCC

    5.3 代码

      1 package com.example.databind.databinding;
      2 import com.example.databind.R;
      3 import com.example.databind.BR;
      4 import androidx.annotation.NonNull;
      5 import androidx.annotation.Nullable;
      6 import android.view.View;
      7 @SuppressWarnings("unchecked")
      8 public class Frgmt1BindingImpl extends Frgmt1Binding  {
      9 
     10     @Nullable
     11     private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
     12     @Nullable
     13     private static final android.util.SparseIntArray sViewsWithIds;
     14     static {
     15         sIncludes = null;
     16         sViewsWithIds = new android.util.SparseIntArray();
     17         sViewsWithIds.put(R.id.imageView, 3);
     18         sViewsWithIds.put(R.id.textView, 4);
     19     }
     20     // views
     21     @NonNull
     22     private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
     23     // variables
     24     // values
     25     // listeners
     26     // Inverse Binding Event Handlers
     27 
     28     public Frgmt1BindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
     29         this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds));
     30     }
     31     private Frgmt1BindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
     32         super(bindingComponent, root, 0
     33             , (android.widget.ImageView) bindings[3]
     34             , (android.widget.TextView) bindings[1]
     35             , (android.widget.TextView) bindings[4]
     36             , (android.widget.TextView) bindings[2]
     37             );
     38         this.key.setTag(null);
     39         this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
     40         this.mboundView0.setTag(null);
     41         this.value.setTag(null);
     42         setRootTag(root);
     43         // listeners
     44         invalidateAll();
     45     }
     46 
     47     @Override
     48     public void invalidateAll() {
     49         synchronized(this) {
     50                 mDirtyFlags = 0x4L;
     51         }
     52         requestRebind();
     53     }
     54 
     55     @Override
     56     public boolean hasPendingBindings() {
     57         synchronized(this) {
     58             if (mDirtyFlags != 0) {
     59                 return true;
     60             }
     61         }
     62         return false;
     63     }
     64 
     65     @Override
     66     public boolean setVariable(int variableId, @Nullable Object variable)  {
     67         boolean variableSet = true;
     68         if (BR.click == variableId) {
     69             setClick((com.example.databind.Click) variable);
     70         }
     71         else if (BR.data == variableId) {
     72             setData((com.example.databind.Data) variable);
     73         }
     74         else {
     75             variableSet = false;
     76         }
     77             return variableSet;
     78     }
     79 
     80     public void setClick(@Nullable com.example.databind.Click Click) {
     81         this.mClick = Click;
     82     }
     83     public void setData(@Nullable com.example.databind.Data Data) {
     84         this.mData = Data;
     85         synchronized(this) {
     86             mDirtyFlags |= 0x2L;
     87         }
     88         notifyPropertyChanged(BR.data);
     89         super.requestRebind();
     90     }
     91 
     92     @Override
     93     protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
     94         switch (localFieldId) {
     95         }
     96         return false;
     97     }
     98 
     99     @Override
    100     protected void executeBindings() {
    101         long dirtyFlags = 0;
    102         synchronized(this) {
    103             dirtyFlags = mDirtyFlags;
    104             mDirtyFlags = 0;
    105         }
    106         java.lang.String stringValueOfDataValue = null;
    107         java.lang.String dataKey = null;
    108         com.example.databind.Data data = mData;
    109         int dataValue = 0;
    110 
    111         if ((dirtyFlags & 0x6L) != 0) {
    112 
    113 
    114 
    115                 if (data != null) {
    116                     // read data.key
    117                     dataKey = data.key;
    118                     // read data.value
    119                     dataValue = data.value;
    120                 }
    121 
    122 
    123                 // read String.valueOf(data.value)
    124                 stringValueOfDataValue = java.lang.String.valueOf(dataValue);
    125         }
    126         // batch finished
    127         if ((dirtyFlags & 0x6L) != 0) {
    128             // api target 1
    129 
    130             androidx.databinding.adapters.TextViewBindingAdapter.setText(this.key, dataKey);
    131             androidx.databinding.adapters.TextViewBindingAdapter.setText(this.value, stringValueOfDataValue);
    132         }
    133     }
    134     // Listener Stub Implementations
    135     // callback impls
    136     // dirty flag
    137     private  long mDirtyFlags = 0xffffffffffffffffL;
    138     /* flag mapping
    139         flag 0 (0x1L): click
    140         flag 1 (0x2L): data
    141         flag 2 (0x3L): null
    142     flag mapping end*/
    143     //end
    144 }

    5.4 可以直接使用 ViewDataBinding

    1     private ViewDataBinding binding;
    2 
    3     private void init(View view){
    4         binding = DataBindingUtil.bind(view);
    5         MainActivity main = (MainActivity) getActivity();
    6         binding.setVariable(BR.data,main.data);
    7     }

     

  • 相关阅读:
    Java自带工具jstack故障分析的一个案例
    当spring 容器初始化完成后执行某个方法
    tomcat的maxThreads、acceptCount(最大线程数、最大排队数)
    RESTful API 设计指南
    GitHub简单使用入门
    Newton's method
    把一个文件夹下的多个excel文件合并到同一个excel的一个sheet里
    multimap的使用 in C++,同一个关键码存在多个值
    pandas把多个sheet读进一个DataFrame
    数据预处理之Minkowski距离计算
  • 原文地址:https://www.cnblogs.com/mhbs/p/11621078.html
Copyright © 2011-2022 走看看