<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      BasicSample項目說明

      2023-07-30 11:31  ttylinux  閱讀(50)  評論(0)    收藏  舉報
      整個示例項目,兩個Fragment,ProductListFragment和ProductFragment,一個MainActivity。在MainActivity里面展示的是ProductListFragment,點擊每個Item,
      會進入相應的ProductFragment。
       
      相關技術點說明:
      LiveData應用:
      LiveData的應用,當LiveData包裝的數據發生變化的時候,更新List
      private void subscribeUi(LiveData<List<ProductEntity>> liveData) {
          // Update the list when the data changes
          liveData.observe(getViewLifecycleOwner(), myProducts -> {
              if (myProducts != null) {
                  mBinding.setIsLoading(false);
                  mProductAdapter.setProductList(myProducts);
              } else {
                  mBinding.setIsLoading(true);
              }
              // espresso does not know how to wait for data binding's loop so we execute changes
              // sync.
              mBinding.executePendingBindings();
          });
      }
      
      保存LiveData的狀態數據,并且根據狀態數據的變化,更新LiveData:
      Transformations.switchMap和SavedStateHandle的應用
      public ProductListViewModel(@NonNull Application application,
              @NonNull SavedStateHandle savedStateHandle) {
          super(application);
          mSavedStateHandler = savedStateHandle;
      
      
          mRepository = ((BasicApp) application).getRepository();
      
      
          // Use the savedStateHandle.getLiveData() as the input to switchMap,
          // allowing us to recalculate what LiveData to get from the DataRepository
          // based on what query the user has entered
          //LiveData<String> query,數據發生變化,就會觸發執行向數據庫查詢的動作,將查詢結果添加到已有的LiveData中
          mProducts = Transformations.switchMap(
                  savedStateHandle.getLiveData("QUERY", null),
                  (Function<CharSequence, LiveData<List<ProductEntity>>>) query -> {
                      if (TextUtils.isEmpty(query)) {
                          return mRepository.getProducts();
                      }
                      return mRepository.searchProducts("*" + query + "*");
                  });
      }
      
      
      public void setQuery(CharSequence query) {
          // Save the user's query into the SavedStateHandle.
          // This ensures that we retain the value across process death
          // and is used as the input into the Transformations.switchMap above
          //保存在SavedStateHandler的數據,即使進程銷毀重建都會存在
          mSavedStateHandler.set(QUERY_KEY, query);
      }
      

       實際的數據獲取實現,封裝在DataRepository mRepository;中。

       

      RecyclerView - DiffUtil 局部刷新
       
      輸入兩個數據集合,計算兩個數據集合的差異化,然后根據差異化結果更新列表。差異化的計算過程是在UI線程中進行的。
      如果數據量巨大,可以在異步線程更新,使用AsyncListDiffer和ListAdapter。
      public void setProductList(final List<? extends Product> productList) {
          if (mProductList == null) {
              mProductList = productList;
              notifyItemRangeInserted(0, productList.size());
          } else {
              DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                  @Override
                  public int getOldListSize() {
                      return mProductList.size();
                  }
      
      
                  @Override
                  public int getNewListSize() {
                      return productList.size();
                  }
      
      
                  @Override
                  public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                      return mProductList.get(oldItemPosition).getId() ==
                              productList.get(newItemPosition).getId();
                  }
      
      
                  @Override
                  public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                      Product newProduct = productList.get(newItemPosition);
                      Product oldProduct = mProductList.get(oldItemPosition);
                      return newProduct.getId() == oldProduct.getId()
                              && TextUtils.equals(newProduct.getDescription(), oldProduct.getDescription())
                              && TextUtils.equals(newProduct.getName(), oldProduct.getName())
                              && newProduct.getPrice() == oldProduct.getPrice();
                  }
              });
              mProductList = productList;
              result.dispatchUpdatesTo(this);
          }
      }
      
      回調函數方法的含義
      public abstract static class Callback {
          /**
          * 舊數據 size
          */
          public abstract int getOldListSize();
          /**
          * 新數據 size
          */
          public abstract int getNewListSize();
          /**
          * DiffUtil 調用判斷兩個 itemview 對應的數據對象是否一樣. 由于 DiffUtil 是對兩個不同數據集合的對比, 所以比較對象引用肯定是不行的, 一般會使用 id 等具有唯一性的字段進行比較.
         
          * @param oldItemPosition 舊數據集合中的下標
          * @param newItemPosition 新數據集合中的下標
          * @return True 返回 true 即判斷兩個對象相等, 反之則是不同的兩個對象.
          */
          public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
          /**
          * Diffutil 調用判斷兩個相同對象之間的數據是否不同. 此方法僅會在 areItemsTheSame() 返回 true 的情況下被調用.
          *
          * @param oldItemPosition 舊數據集合中的下標
          * @param newItemPosition 新數據集合中用以替換舊數據集合數據項的下標
          * @return True 返回 true 代表 兩個對象的數據相同, 反之則有差別.
          */
          public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
          /**
          * 當 areItemsTheSame() 返回true areContentsTheSame() 返回 false 時, 此方法將被調用, 來完成局部刷新功能.
          */
          @Nullable
          public Object getChangePayload(int oldItemPosition, int newItemPosition);
      }
      

       DataBinding,數據綁定

      <layout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto">
          <data>
              <variable name="product"
                        type="com.example.android.persistence.model.Product"/>
              <variable name="callback"
                        type="com.example.android.persistence.ui.ProductClickCallback"/>
          </data>
      
      
          <androidx.cardview.widget.CardView
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:minHeight="@dimen/product_item_min_height"
              android:onClick="@{() ->  callback.onClick(product)}"
              android:orientation="horizontal"
              android:layout_marginStart="@dimen/item_horizontal_margin"
              android:layout_marginEnd="@dimen/item_horizontal_margin"
              app:cardUseCompatPadding="true">
      
      
              <RelativeLayout
                  android:layout_marginStart="@dimen/item_horizontal_margin"
                  android:layout_marginEnd="@dimen/item_horizontal_margin"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content">
      
      
                  <TextView
                      android:id="@+id/name"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:contentDescription="@string/cd_product_name"
                      android:text="@{product.name}"/>
      
      
                  <TextView
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:layout_alignParentEnd="true"
                      android:layout_marginEnd="5dp"
                      android:text="@{@string/product_price(product.price)}"/>
      
      
                  <TextView
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:layout_below="@id/name"
                      android:text="@{product.description}"/>
              </RelativeLayout>
      
      
          </androidx.cardview.widget.CardView>
      </layout>
      
      實例化Binding,為Binding注入相應的對象
      @Override
      @NonNull
      public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          ProductItemBinding binding = DataBindingUtil
                  .inflate(LayoutInflater.from(parent.getContext()), R.layout.product_item,
                          parent, false);
          binding.setCallback(mProductClickCallback);
          return new ProductViewHolder(binding);
      }
      
      
      @Override
      public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
          holder.binding.setProduct(mProductList.get(position));
          //立即刷新數據
          holder.binding.executePendingBindings();
      }
      

      =================================================================================================================================

       數據層,創建三個數據表,ProductFtsEntity,ProductEntity,CommentEntity。然后聲明Dao,通過Dao來獲取數據庫中的數據,與數據庫交互。
      Dao:
      @Dao
      public interface CommentDao {
          @Query("SELECT * FROM comments where productId = :productId")
          LiveData<List<CommentEntity>> loadComments(int productId);
      
      
          @Query("SELECT * FROM comments where productId = :productId")
          List<CommentEntity> loadCommentsSync(int productId);
      
      
          @Insert(onConflict = OnConflictStrategy.REPLACE)
          void insertAll(List<CommentEntity> comments);
      }
      
      應用@TypeConverter,在聲明Entity的時候,使用屬性類型不是基本類型時,是復雜的數據類型時,比如是Date類型時,需要
      定義TypeConverter,這樣數據庫才知道如何存儲該數據。
      定義TypeConverter需要實現兩個方法,一個是從基礎數據獲取復雜數據的方法,一個是復雜數據轉換為基礎數據的方法,比如對于類型Date:
      public class Converters {
        @TypeConverter
        public static Date fromTimestamp(Long value) {
          return value == null ? null : new Date(value);
        }
      
      
        @TypeConverter
        public static Long dateToTimestamp(Date date) {
          return date == null ? null : date.getTime();
        }
      }
      
      數據庫實例,設計為一個單例,使用單例模式:
      單例模式,設計為線程安全的,首先檢測是否為null,二次檢測,加鎖,判斷是否為null,如果為null,
      則在當前線程創建數據庫實例。
      public static AppDatabase getInstance(final Context context, final AppExecutors executors) {
          if (sInstance == null) {
              synchronized (AppDatabase.class) {
                  if (sInstance == null) {
                      sInstance = buildDatabase(context.getApplicationContext(), executors);
                      sInstance.updateDatabaseCreated(context.getApplicationContext());
                  }
              }
          }
          return sInstance;
      }
      
      通過Room.databaseBuilder,builder模式構建數據庫實例。
      在構建的時候,添加回調方法,首次創建的時候,數據庫創建成功,則在第三方線程為數據庫插入數據,也就是在onCreate方法里面執行。
      添加數據庫升級策略。
      /**
      * Build the database. {@link Builder#build()} only sets up the database configuration and
      * creates a new instance of the database.
      * The SQLite database is only created when it's accessed for the first time.
      */
      private static AppDatabase buildDatabase(final Context appContext,
              final AppExecutors executors) {
          return Room.databaseBuilder(appContext, AppDatabase.class, DATABASE_NAME)
                  .addCallback(new Callback() {
                      @Override
                      public void onCreate(@NonNull SupportSQLiteDatabase db) {
                          super.onCreate(db);
                          executors.diskIO().execute(() -> {
                              // Add a delay to simulate a long-running operation
                              addDelay();
                              // Generate the data for pre-population
                              AppDatabase database = AppDatabase.getInstance(appContext, executors);
                              List<ProductEntity> products = DataGenerator.generateProducts();
                              List<CommentEntity> comments =
                                      DataGenerator.generateCommentsForProducts(products);
      
      
                              insertData(database, products, comments);
                              // notify that the database was created and it's ready to be used
                              database.setDatabaseCreated();
                          });
                      }
                  })
              .addMigrations(MIGRATION_1_2)
              .build();
      }
      

        

      數據庫升級策略,添加Migrations,如下,從版本1升級到版本2,執行如下SQL語句:
      如果不存在productFts數據庫表,則創建它;并且從products表查詢數據來填充productFts數據表。
      private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
      
      
          @Override
          public void migrate(@NonNull SupportSQLiteDatabase database) {
              database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `productsFts` USING FTS4("
                  + "`name` TEXT, `description` TEXT, content=`products`)");
              database.execSQL("INSERT INTO productsFts (`rowid`, `name`, `description`) "
                  + "SELECT `id`, `name`, `description` FROM products");
      
      
          }
      };
      

        

      主站蜘蛛池模板: 午夜亚洲国产理论片亚洲2020| 蓝山县| 国产午夜精品无码一区二区| 亚洲无人区码一二三四区| 一个人看的www免费高清视频| 欧美疯狂xxxxxbbbbb| 成人动漫在线观看| 国产午夜精品理论大片| 日韩内射美女人妻一区二区三区 | 欧美高清精品一区二区| 精选国产av精选一区二区三区| 日韩a∨精品日韩在线观看| 国产精品一区二区三区黄色| 午夜性色一区二区三区不卡视频| 66亚洲一卡2卡新区成片发布| 亚洲一区二区av在线| 日本一区二区三区专线| 丁香五月亚洲综合在线| 成年午夜无码av片在线观看| 国产高清精品一区二区三区 | 最新亚洲春色av无码专区| 国产午夜福利一区二区三区| 亚洲中文字幕国产综合| 亚洲丶国产丶欧美一区二区三区| 免费国产高清在线精品一区| 久久精品国产午夜福利伦理 | 九九热精品视频在线免费| 国产亚洲无线码一区二区| 日本中文字幕在线播放| 国产四虎永久免费观看| 国产福利精品一区二区| 成人免费无遮挡在线播放| 欧美性猛交xxxx乱大交丰满| 国产精品免费AⅤ片在线观看| 99中文字幕精品国产| 免费拍拍拍网站| 亚洲男人的天堂网站| 亚洲av永久无码精品漫画| 熟妇人妻系列aⅴ无码专区友真希| 国产精成人品日日拍夜夜| 国产乱子伦视频在线播放|