Android -- View , 自定义View,ListView,RecyclerView,ViewStub

自定义View

布局,绘制,触摸反馈

View的绘制流程

1.根部:

Android framework 处理是从根节点开始,对布局进行measure和draw ,整个View树的绘制流程在ViewRoot.java类的performTraversals()函数展开
performTraversals:
1.是否需要重新计算视图大小measure,2.是否需要重新安置视图的位置layout,3.是否需要重新绘制draw

(measure测量)大小 -->(layout安排)位置 --> (draw绘制)内容
Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第1张图片

绘制顺序
Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第2张图片

  • DecorView是根布局
  • 然后按照树形图遍历,父view要measure自己之后再去遍历子view

measure 过程传递的两个类

  • ViewGroup.LayoutParams(View自身的布局参数)
    用来指定高度和宽度,getLayoutParams()会得到父view的layoutParams
  • MeasureSpecs类(父视图对自视图的测量要求)
    测量和尺寸要求,有三个模式
    UNSPECIFIED,没有约束
    EXACTLY 确定尺寸
    AT_MOST 父控件所给的最大尺寸,通常自定义

2.绘制流程

Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第3张图片

2.1 measure流程
  • view的 measure方法
    如果只是高度宽度的话,就需要super.onMeasure
    如果完全自定义,不需要super
    记得set
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  1. measure(int,int) 调用onMeasure(int,int),但是measure是final类,所以子类必须复写onMeasure。
    onMeasure:
    setMeasuredDimension():设置view的宽,高
    getDefaultSize():返回测量后的大小,或者mMinWidth和Drawable最小宽度的最大值
  • ViewGrop的 measure方法
    ViewGrop没有onMeasure()方法,但是定义了measureChildren
    measureChildren:遍历所有children,获得子元素的属性
  • LinearLayout的measure流程
    LinearLayout 是 ViewGrop的子类,他去实现ViewGrop没有实现的onMeasure,
    mTotalLength 存储垂直高度,
    wrap_content:计算所有元素,求和
    match_parent:和view的测量一样
  • View的layout流程
    传进来四个坐标点,坐标是对父布局来说的,并不是绝对做坐标
    setFrame:设置四个顶点,然后执行onLayout
public void layout(int l, int t, int r, int b) {
     
...省略
 boolean changed = isLayoutModeOptical(mParent) ?
         setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
     
            onLayout(changed, l, t, r, b);
...省略          
    }

onLayout: layoutVertical(l, t, r, b);或者 layoutHorizontal(l, t, r, b);
setChildFrame,遍历子布局

2.2 layout流程
2.3 draw流程
@Override
    protected void onDraw(Canvas canvas) {
     
        super.onDraw(canvas);
    }
    /*
    // 把 onDraw() 换成了 dispatchDraw()
    protected void dispatchDraw(Canvas canvas) {
       super.dispatchDraw(canvas);
    
       ... // 绘制斑点,覆盖在原有的图片上
    }
    */

Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第4张图片
Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第5张图片
Android -- View , 自定义View,ListView,RecyclerView,ViewStub_第6张图片

  • View.draw(Canvas canvas)
    ViewGrop没有复写,需要View.draw,但是自定义view一般是用onDraw进行绘制,也可以先进行super.draw进行系统的draw绘制,在进行自定义的onDraw绘制
    canvas的绘制:
    canvas.drawCircle(3,3,5,p);//画一个圆,(坐标,半径,颜料)Paint:颜色风格都可以,绘制非常关键
    canvas.drawBitmap();
    canvas.drawPath();
    canvas.drawRect();矩形
    canvas.drawText();

  • View.onDraw()
    默认空实现自定义需要自己复写方法,自己绘制

  • dispachDraw
    viewGrop对子视图的绘制,自定义的ViewGrop不应该对dispachDraw进行复写

  • drawChild(cavans,this,drawingTime)
    调用了 View 的child.draw(canvas, this,drawingTime)方法,

  • invalidate
    重绘draw树

dispatchDraw()–>drawChild()–>view.draw()

  • draw六个步骤
    1.若有背景则绘制
    2.保存canvas层
    3.绘制自身内容
    4.如果有子元素则绘制子元素
    5.绘制效果
    6.绘制装饰品(scrollbars)

二 自定义View

https://blog.csdn.net/u010216743/article/details/77744597
https://blog.csdn.net/u010216743/article/details/77744602

1.自定义View需要具备的知识点

  • View坐标系

  • Android的角度与弧度

  • View绘制机制
    一个Activity里最外层包含PhoneWindow,而PhoneWindow里包含DecorView,然后DecorView里包含有TitleViewContentView,对于ContentView,我们立马就会联想到setContentView()方法有木有,其实这个方法就是设置ContentView的布局

  • View事件分发

  • Android动画机制

  • Canvas类

  • Path类

2.View绘制机制

View绘制大致可以分为三个流程,分别是measure(测量),layout(布局),draw(绘制),这三者的顺序就是measure(测量)->layout(布局)->draw(绘制)。

  • measure: 判断是否需要重新计算View的大小,需要的话则计算;测量View的宽和高。
  • layout: 判断是否需要重新计算View的位置,需要的话则计算;
  • draw: 判断是否需要重新绘制View,需要的话则重绘制。

3.三星云的自定义View

  • 自定义圆角。继承自LinearLayout,在ondraw方法中进行了P版本判断
  • 自定义搜索结果的内容,继承自textView。修改了setText方法,用的 SpannableString方法控制搜索结果着色处理。
  • BottomSelectFolderActivity,的布局是自己写的ShrinkHelper,自己只是完善了下滑动等圆角显现操作。
    有对点击焦点的获取,通过ontTouchEvent,传到ShrinkHelper进行处理
    1. getRawX():获取点击事件距离整个屏幕左边距离,即绝对坐标
    2. getRawY():获取点击事件距离整个屏幕顶边的的距离,即绝对坐标

三 ListView RecyclerView

1.ListView

1.1 用法

listData 是一个list,加载到getApplicationContext这个上下文的 r.layout.xxx布局下

listAdapter = new ListAdapter(getApplicationContext(), listview(r.layout.xxx), listData);
listview.setAdapter(listAdapter);

重点
继承重写 BaseAdapter
自定义 ViewHolder 和 convertView 一起完成复用优化工作

1.2 布局设置

addHeaderView()方法:主要是向listView的头部添加布局
addFooterView()方法:主要是向listView的底部添加布局

1.3 优化

   static class ViewHolder{
     
         TextView textView;
         ImageView imageView;
         Button button;
      }
       public View getView(int position, View convertView, ViewGroup parent) {
     
          LayoutInflater inflater = LayoutInflater.from(mContext);
          ViewHolder holder = null;
          if (convertView == null) {
     
              convertView = inflater.inflate(R.layout.item, null);
              holder = new ViewHolder();
              holder.button = (Button) convertView.findViewById(R.id.button);
              holder.textView = (TextView) convertView.findViewById(R.id.textView);
              holder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
              convertView.setTag(holder);
          }    else {
     
              holder = (ViewHolder) convertView.getTag();
          }
          holder.imageView.setImageResource(R.mipmap.ic_launcher);
          holder.button.setOnClickListener(new View.OnClickListener() {
     
              @Override
              public void onClick(View v) {
     
                  Log.d("click","button");
              }
          });
          holder.textView.setText(data[position]);
          return convertView;
    }
  • 1.3.1 ViewHolder机制
    使用ViewHolder,不要在getView方法中写findViewById方法,因为getView方法会执行很多遍,这样也可以节省时间,节约内存。

  • 1.3.2 convertView重用机制
    在Adapter中的getView方法中使用ConvertView,即ConvertView的复用,不需要每次都inflate一个View出来,这样既浪费时间,又浪费内存。

  • 1.3.3 三级缓冲/滑动监听事件

  • 1.3.4 使用分页加载,讲真实际开发中,ListView的数据肯定不止几百条,成千上万条数据你不可能一次性加载出来,所以这里需要用到分页加载,一次加载几条或者十几条,但是如果数据量很大的话,像qq,微信这种,如果顺利加载到最后面的话,那么你的list中也会有几万甚至几十万的数据,这样可能也会导致OOM,所以你的数据集List中也不能有那么多数据,所以每加载一页的时候你可以覆盖前一页的数据

  • 1.3.5 如果数据当中有图片的话,使用第三方库来加载(也就是缓存),如果你的能力强大到能自己维护的话,那也不是不可以。

  • 1.3.6 当你手指在滑动列表的时候,尽可能的不加载图片,这样的话滑动就会更加流畅。

2.RecyclerView

2.1 实现方式:

  • 继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder
  • 设置布局管理器,控制布局效果

2.2 RecyclerView的布局管理器

  • 线性布局管理器(LinearLayoutManager)
  • 网格布局管理器(GridLayoutManager)
  • 瀑布流布局管理器(StaggeredLayouManager)

2.3 RecyclerView的适配器

// 第一步:继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder
       public class AuthorRecyclerAdapter extends RecyclerView.Adapter<AuthorRecyclerAdapter.AuthorViewHolder> {
     
        ...
        @Override
        public AuthorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     
            ...
            return viewHolder;
        }
    
        @Override
        public void onBindViewHolder(AuthorViewHolder holder, int position) {
     
            ...
        }
    
        @Override
        public int getItemCount() {
     
            if (mData == null) {
     
                return 0;
            }
            return mData.size();
        }
    
        class AuthorViewHolder extends RecyclerView.ViewHolder {
     
            ...
            public AuthorViewHolder(View itemView) {
     
                super(itemView);
                ...
            }
        }
       }
    
       mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
       mRecyclerAdapter = new AuthorRecyclerAdapter(mData);
    
       // 第二步:设置布局管理器,控制布局效果
       LinearLayoutManager linearLayoutManager = new LinearLayoutManager(RecyclerDemoActivity.this);
       linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
       mRecyclerView.setLayoutManager(linearLayoutManager);
       mRecyclerView.setAdapter(mRecyclerAdapter);

2.4 优点

  • RecyclerView封装了viewholder的回收复用(listView也有),也就是说RecyclerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
  • 提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecyclerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还StaggeredGridLayoutManager等),也就是说RecyclerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。你想控制Item的分隔线,可以通过继承RecyclerView的ItemDecoration这个类,然后针对自己的业务需求去抒写代码。
  • 可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecyclerView有其自己默认的实现。

2.5 ListView&RecyclerView区别

ListView只支持列表,支持set空页面
RecyclerView支持三中布局,网格,列表,瀑布,不支持set空页面

2.6 优化

结合三星云的,图片和视频的缩略图的加载分析
三星云的Recent的不同区域的Adapter,只是通过设置四个(today,yesterday,7,30,90),这几个Adapter的clear和 set来放入到display中,然后 再把display放入到 driveRecyclerAdapter中,最后recyclerView.set(driveRecyclerAdapter)

ViewStub

动态加载视图,惰性加载,节约内存,提高性能,只加载一次
绘制的时候不会绘制到view树中,代码执行inflate之后才会被添加到视图中,是一个宽高都为0的View,默认Gone,需要SetVisibility或者inflate函数加载设置才能显现,达到延迟效果,提高加载速度
适用范围:网络请求页面失败
第一次加载会把viewStub从父布局中移除,parent.addView()把我们的view加载到父布局Linearlayout中
第二次加载会报错,以为已经移除了,所以不包含viewStub的parent,可以直接findViewById

动画

属性动画
缓速,监听器反馈
运用属性动画的api
imageView.setTranslateX(500),向右移动500

你可能感兴趣的:(Java,Android)