自繪view實現(xiàn)自定義recyclerView的展示

先上圖应又,調(diào)用過程看起來極其復雜彻亲。


類之間的調(diào)用關(guān)系

類雖然很多,但是按照功能作用可以分成幾個模塊

  • 負責給RecyclerView傳遞數(shù)據(jù)的Adaptor
  • 負責展示的View
  • 負責傳包裝繪制信息的Canvas類

關(guān)于ViewHolder的疑惑

在使用RecyclerView無可避免的要使用Adapter來進行數(shù)據(jù)的存放,而Adapter是有固定寫法的缝裤,一般需要onCreateViewHolder來創(chuàng)建ViewHolder和OnBindViewHolder來綁定數(shù)據(jù)件缸,但是我發(fā)現(xiàn)在我學習的項目中铜靶,這兩個方法的參數(shù)和數(shù)據(jù)的綁定有所區(qū)別,所以他炊,adapter到底是根據(jù)什么來綁定要顯示的item的呢争剿?

  • 標準版:

    @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閃爍問題
    }
    class ViewHolder extends RecyclerView.ViewHolder {
    
      private ItemListBinding binding;
    
      public ItemListBinding getBinding() {
          return binding;
      }
    
      public ViewHolder(@NonNull ItemListBinding binding) {
          super(binding.getRoot());
          this.binding = binding;
      }
    }
    
  • 項目版

    //子類分別實現(xiàn)
    @Override
     public ViewModelHolder onCreateViewHolder(ViewGroup parent, int innerViewType)
     {
        return new ViewModelHolder(TVViewModelFactory.create(parent, innerViewType));
     }
     //父類統(tǒng)一實現(xiàn)
      @Override
    public void onBindViewHolder(ViewModelHolder holder, int position, List<Object> payloads)
    {
        super.onBindViewHolder(holder, position, payloads);
        if (holder.getAsyncState() != TvViewHolder.ASYNC_STATE_SUCC)
        {
           updateData(position, getItem(position), holder.getViewModel());
        }
        final TVLifecycleOwner source = getTVLifecycleOwner();
        if (source != null)
        {
            holder.getViewModel().onBind(source);
        }
        else
        {
            mModelGroup.add(holder.getViewModel());
        }
    }
    

項目中的思想是,統(tǒng)一實現(xiàn)一個專用于ViewModel的Adapter痊末,在這些Adapter中使用的都是ViewModel蚕苇,所以傳入ViewHolder的是ViewModel。
其實可以發(fā)現(xiàn)凿叠,無論在onCreateViewHolder中傳入的參數(shù)是DataBinding還是ViewModel涩笤,只要在自定義的ViewHolder的構(gòu)造方法中把要顯示的View傳進就可以了,這可以在源碼中發(fā)現(xiàn)盒件,

  • 源碼

    public ViewHolder(View itemView) {
          if (itemView == null) {
              throw new IllegalArgumentException("itemView may not be null");
          }
          this.itemView = itemView;
      }
    
  • 當參數(shù)為ViewModel時自定義的ViewHolder調(diào)用的super

    //子類
    public ViewModelHolder(@NonNull TVViewModel viewModel)
    {
       super(viewModel.getRootView());
       mViewModel = viewModel;
    }
    //父類
    public TvViewHolder(View itemView)
    {
        super(itemView);
    }
    
  • 當參數(shù)為DataBinding時的super

    public ViewHolder(@NonNull ItemListBinding binding) {
         super(binding.getRoot());
         this.binding = binding;
     }
    

其實發(fā)現(xiàn)蹬碧,無論自定義的ViewHolder傳入的參數(shù)是什么類型的,調(diào)用父類的構(gòu)造方法的時候履恩,傳入的都是view锰茉。所以只要獲取的參數(shù)中的view傳給父類構(gòu)造方法,也就完成了綁定切心。

Adaptor部分

RowItemAdapter繼承于AnsyncListVMAdapter繼承于ViewModelAdapter飒筑,ViewModelAdapter是所有使用ViewModel的Adapter的父類,onBindViewHolder在這個類中實現(xiàn)绽昏,統(tǒng)一進行數(shù)據(jù)綁定协屡,
RowItemAdapter和其他子類中實現(xiàn)了onCreateViewHoler方法,用于每個不同的子類調(diào)用工廠以創(chuàng)建出不同的符合條件的ViewModel全谤,這兩個方法在上面已經(jīng)貼出肤晓,不展示了。但是onCreateViewHoler用于構(gòu)建合適的ViewModel的標志innerViewType是從何而來呢,從同一個類的如下方法中返回补憾,

@Override
public int getItemViewType(int position)
{
    final RowItem item = getItem(position);
    return item == null ? ViewType._VIEW_TYPE_EMPTY : item.mInnerViewType;
}

實際上傳入Adapter的數(shù)據(jù)是元素為RowItem的Array漫萄,而這個RowItem是一個抽象類,所以Array中的數(shù)據(jù)是存放了一系列實現(xiàn)了特定抽象方法的對象盈匾,看看這個RowItem類

public abstract class RowItem
{
   public final int mInnerViewType;

   public RowItem(int innerViewType)
   {
      this.mInnerViewType = innerViewType;
    }

    public abstract void updateViewData(@NonNull TVViewModel model);
 }

可以看到實現(xiàn)這個類中有mInnerViewType變量腾务,所以,數(shù)組中每一個元組都有這個值削饵,根據(jù)position獲取到特定位置的元素岩瘦,即可獲取到type從而創(chuàng)建ViewModel,在下面即將介紹的CanvasRowItem就必須要繼承于RowItem窿撬,才能作為數(shù)據(jù)集被傳到Adapter中启昧,在RowItem中有一個updateViewData方法,也就是子類要實現(xiàn)的抽象方法劈伴,用于進行數(shù)據(jù)更新密末。

自繪View--Canvas部分

  • CanvasNode繪制信息類,提供了用于繪制各種情況下的繪制方法宰啦,
    舉兩個例子苏遥,實際上對于不同狀態(tài)只是返回一個含有不同狀態(tài)變量的對象

    public static <T extends BaseCanvas> CanvasNode<T> focused(@NonNull CanvasBuilder<T> canvas)
    {
         return new CanvasNode<>(CanvasState.VIEW_FOCUSED, canvas, null);
     }
    public static <T extends BaseCanvas> CanvasNode<T> normal(@NonNull CanvasBuilder<T> canvas, @NonNull CanvasRefresher<T> refresher)
    {
      return new CanvasNode<>(CanvasState.NONE, canvas, refresher);
    }
    //構(gòu)造方法,傳入state
    private CanvasNode(int state, @NonNull CanvasBuilder<T> builder, @Nullable CanvasRefresher<T> refresher)
    {
        mState = state;
        mCanvasBuilder = builder;
        mRefresher = refresher;
    }
    
  • CanvasBundle容器類赡模,用于封裝CanvasNode的第一層容器

  • CanvasBundleExt工廠類田炭,用于創(chuàng)建各種用途的CanvasBundle,例如按鈕類漓柑,文本類教硫,

     public static CanvasBundle createItem(long hash, String logoUrl, String content)
    {
      final int width = 541;
      final int height = 80;
      return new CanvasBundle(hash, hash, width, height, Arrays.asList(
              // 背景
              CanvasNode.focused((context, bundle) -> buildViewBg(R.drawable.common_view_bg_normal, width, height, context)),
              // 文字
              CanvasNode.focused((context, bundle) -> {
                  final TextCanvas title = new TextCanvas();
                  title.setDesignTextSize(32);
                  title.setText(content);
                  title.setTextColor(ContextCompat.getColor(context, R.color.ui_color_white_100));
                  title.setMaxLines(1);
                  title.setMaxDesignWidth(width - 65 - 30);
                  title.setEllipsize(TextUtils.TruncateAt.MARQUEE);
                  title.setMarqueeRepeatLimit(TextCanvas.MARQUEE_REPEAT_FOREVER);
                  final int textHeight = title.getTextDesignHeight();
                  title.setDesignRect(
                          65,
                          (height - textHeight) >> 1,
                          width - 30,
                          (height + textHeight) >> 1);
                  title.getTextDesignHeight(); // 消除dirty
                  return title;
              }),
              CanvasNode.normal((context, bundle) -> {
                  final TextCanvas title = new TextCanvas();
                  title.setDesignTextSize(32);
                  title.setText(content);
                  title.setTextColor(ContextCompat.getColor(context, R.color.ui_color_white_80));
                  title.setMaxLines(1);
                  title.setMaxDesignWidth(width - 65 - 30);
                  final int textHeight = title.getTextDesignHeight();
                  title.setDesignRect(
                          65,
                          (height - textHeight) >> 1,
                          width - 30,
                          (height + textHeight) >> 1);
                  title.getTextDesignHeight(); // 消除dirty
                  return title;
              }),
              CanvasNode.focused((context, bundle) -> buildLightAnim(width, height, context)).disableExternalAlpha()
      )).setFocusScale(1.05f).setTopMargin(8).setBottomMargin(8);
    }
    

實際上就是把所有的CanvasNode作為元素存儲在CanvasBundle的數(shù)組變量中,

  • CanvasViewModel用于獲取實際顯示的View辆布,在onCreateViewHolder中調(diào)用瞬矩,

     @Override
    public void initView(@NonNull ViewGroup parent)
    {
       mView = new CanvasView(parent.getContext());
       mView.setLayoutParams(new  GridLayoutManager.LayoutParams(GridLayoutManager.LayoutParams.WRAP_CONTENT, GridLayoutManager.LayoutParams.WRAP_CONTENT));
       setRootView(mView);
    }
     @Override
    public void updateViewData(@NonNull CanvasBundle data)
    {
        super.updateViewData(data);
        mView.setCanvasBundle(data);
        setItemInfo(data.mItemInfo);
        mRunnable = data.mRunnable;
        setFocusScale(data.mFocusScale);
     } 
    

View部分

CanvasView繼承自SpecifySizeView繼承自View

  • CanvasView 在CanvasViewModel的初始化過程中被實例化,并在CanvasViewModel的updataViewData中注入CanvasBundle,

    @Override
    protected void onDrawNormal(Canvas canvas)
    {
      if (mBundle != null && mBundle.mPivotX != Integer.MIN_VALUE)
      {
          setPivotX(mBundle.mPivotX);
      }
      else
      {
          setPivotX(getWidth() >> 1);
      }
      if (mBundle != null && mBundle.mPivotY != Integer.MIN_VALUE)
      {
          setPivotY(mBundle.mPivotY);
      }
      else
      {
          setPivotY(getHeight() >> 1);
      }
      if (mBundle != null)
      {
          mDrawingRecord.clear();
          if (mBundle.onDraw(getCanvasState(), canvas, mDrawingRecord))
          {
              removeCallbacks(mInvalidateRunnable);
          }
          else
          {
              postDelayed(mInvalidateRunnable, 500/*ms*/);
          }
      }
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锋玲,一起剝皮案震驚了整個濱河市景用,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惭蹂,老刑警劉巖伞插,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盾碗,居然都是意外死亡媚污,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門廷雅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耗美,“玉大人京髓,你說我怎么就攤上這事∩碳埽” “怎么了堰怨?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甸私。 經(jīng)常有香客問我诚些,道長,這世上最難降的妖魔是什么皇型? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮砸烦,結(jié)果婚禮上弃鸦,老公的妹妹穿的比我還像新娘。我一直安慰自己幢痘,他們只是感情好唬格,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颜说,像睡著了一般购岗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上门粪,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天喊积,我揣著相機與錄音,去河邊找鬼玄妈。 笑死乾吻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拟蜻。 我是一名探鬼主播绎签,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酝锅!你這毒婦竟也來了诡必?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤搔扁,失蹤者是張志新(化名)和其女友劉穎爸舒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁谆,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡碳抄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了场绿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剖效。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出璧尸,到底是詐尸還是另有隱情咒林,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布爷光,位于F島的核電站垫竞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛀序。R本人自食惡果不足惜欢瞪,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徐裸。 院中可真熱鬧遣鼓,春花似錦、人聲如沸重贺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽气笙。三九已至次企,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潜圃,已是汗流浹背缸棵。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秉犹,地道東北人蛉谜。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像崇堵,于是被迫代替她去往敵國和親型诚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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

  • 詳情頁面 packagecom.example.shoppingcar; importandroid.conten...
    ForCrazyLove閱讀 557評論 0 2
  • 生活本來就不容易,而我們的不努力只會讓生活變得更加無賴 前幾天依據(jù)《Android群英傳》的學習寫了一篇筆記是關(guān)于...
    AmatorLee閱讀 3,731評論 7 23
  • 這篇文章分三個部分鸳劳,簡單跟大家講一下 RecyclerView 的常用方法與奇葩用法狰贯;工作原理與ListView比...
    LucasAdam閱讀 4,377評論 0 27
  • 懶得處理樣式了, 將就著看吧. 官網(wǎng)地址: https://developer.android.com/topic...
    Reddington_604e閱讀 1,645評論 0 1
  • 花園 Garten 旅行 reisen夏季 Sommer我旅行 Ich reise你旅行 Du reist他旅行...
    AK蝸牛閱讀 316評論 0 0