美文网首页安卓
自绘view实现自定义recyclerView的展示

自绘view实现自定义recyclerView的展示

作者: 小熊兜里有糖 | 来源:发表于2019-07-03 20:36 被阅读0次

    先上图,调用过程看起来极其复杂。


    类之间的调用关系

    类虽然很多,但是按照功能作用可以分成几个模块

    • 负责给RecyclerView传递数据的Adaptor
    • 负责展示的View
    • 负责传包装绘制信息的Canvas类

    关于ViewHolder的疑惑

    在使用RecyclerView无可避免的要使用Adapter来进行数据的存放,而Adapter是有固定写法的,一般需要onCreateViewHolder来创建ViewHolder和OnBindViewHolder来绑定数据,但是我发现在我学习的项目中,这两个方法的参数和数据的绑定有所区别,所以,adapter到底是根据什么来绑定要显示的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;
        }
      }
      
    • 项目版

      //子类分别实现
      @Override
       public ViewModelHolder onCreateViewHolder(ViewGroup parent, int innerViewType)
       {
          return new ViewModelHolder(TVViewModelFactory.create(parent, innerViewType));
       }
       //父类统一实现
        @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());
          }
      }
      

    项目中的思想是,统一实现一个专用于ViewModel的Adapter,在这些Adapter中使用的都是ViewModel,所以传入ViewHolder的是ViewModel。
    其实可以发现,无论在onCreateViewHolder中传入的参数是DataBinding还是ViewModel,只要在自定义的ViewHolder的构造方法中把要显示的View传进就可以了,这可以在源码中发现,

    • 源码

      public ViewHolder(View itemView) {
            if (itemView == null) {
                throw new IllegalArgumentException("itemView may not be null");
            }
            this.itemView = itemView;
        }
      
    • 当参数为ViewModel时自定义的ViewHolder调用的super

      //子类
      public ViewModelHolder(@NonNull TVViewModel viewModel)
      {
         super(viewModel.getRootView());
         mViewModel = viewModel;
      }
      //父类
      public TvViewHolder(View itemView)
      {
          super(itemView);
      }
      
    • 当参数为DataBinding时的super

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

    其实发现,无论自定义的ViewHolder传入的参数是什么类型的,调用父类的构造方法的时候,传入的都是view。所以只要获取的参数中的view传给父类构造方法,也就完成了绑定。

    Adaptor部分

    RowItemAdapter继承于AnsyncListVMAdapter继承于ViewModelAdapter,ViewModelAdapter是所有使用ViewModel的Adapter的父类,onBindViewHolder在这个类中实现,统一进行数据绑定,
    RowItemAdapter和其他子类中实现了onCreateViewHoler方法,用于每个不同的子类调用工厂以创建出不同的符合条件的ViewModel,这两个方法在上面已经贴出,不展示了。但是onCreateViewHoler用于构建合适的ViewModel的标志innerViewType是从何而来呢,从同一个类的如下方法中返回,

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

    实际上传入Adapter的数据是元素为RowItem的Array,而这个RowItem是一个抽象类,所以Array中的数据是存放了一系列实现了特定抽象方法的对象,看看这个RowItem类

    public abstract class RowItem
    {
       public final int mInnerViewType;
    
       public RowItem(int innerViewType)
       {
          this.mInnerViewType = innerViewType;
        }
    
        public abstract void updateViewData(@NonNull TVViewModel model);
     }
    

    可以看到实现这个类中有mInnerViewType变量,所以,数组中每一个元组都有这个值,根据position获取到特定位置的元素,即可获取到type从而创建ViewModel,在下面即将介绍的CanvasRowItem就必须要继承于RowItem,才能作为数据集被传到Adapter中,在RowItem中有一个updateViewData方法,也就是子类要实现的抽象方法,用于进行数据更新。

    自绘View--Canvas部分

    • CanvasNode绘制信息类,提供了用于绘制各种情况下的绘制方法,
      举两个例子,实际上对于不同状态只是返回一个含有不同状态变量的对象

      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);
      }
      //构造方法,传入state
      private CanvasNode(int state, @NonNull CanvasBuilder<T> builder, @Nullable CanvasRefresher<T> refresher)
      {
          mState = state;
          mCanvasBuilder = builder;
          mRefresher = refresher;
      }
      
    • CanvasBundle容器类,用于封装CanvasNode的第一层容器

    • CanvasBundleExt工厂类,用于创建各种用途的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的数组变量中,

    • CanvasViewModel用于获取实际显示的View,在onCreateViewHolder中调用,

       @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*/);
            }
        }
      }
      

    相关文章

      网友评论

        本文标题:自绘view实现自定义recyclerView的展示

        本文链接:https://www.haomeiwen.com/subject/fjcchctx.html