0
点赞
收藏
分享

微信扫一扫

STM-32:SPI通信外设

忍禁 2023-05-11 阅读 77
android

View基础(25题)

  • 什么是View
  • View的位置参数
  • MotionEvent
  • ViewRoot
  • DecorView
  • MeasureSpec

View三大流程(28题)

  • measure过程
  • View
  • ViewGroup
  • layout过程
  • draw过程
  • 获取View的宽高
  • Activity启动到加载ViewRoot的流程

自定义View(26题)

  • 四种实现方法
  • 直接继承View
  • 自定义属性
  • 直接继承ViewGroup
  • 性能优化
  • 硬件加速

事件分发机制(22题)

  • 三个重要方法
  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent
  • 事件传递规则与要点
  • 事件传递规则
  • Activity的事件分发
  • Window的事件分发
  • DecorView的事件分发
  • 根View的事件分发
  • ViewGroup的事件分发
  • View的事件分发和事件处理

滑动冲突(8题)

  • 滑动冲突的三种场景
  • 滑动冲突处理原则和解决办法
  • 外部拦截
  • 内部拦截

滑动(39题)

  • 滑动的7种实现方法
  • 弹性滑动
  • Scroller
  • 动画
  • 延时策略
  • 侧滑菜单
  • DraweLayout
  • SlidingPanelLayout
  • NavigationView
  • ViewDragHelper
  • ViewDragHelper.Callback
  • GestureDetector
  • OnGestureListener
  • OnDoubleTapListener
  • OnContextClickListener
  • SimpleOnGestureListener

辅助类

  • ViewConfiguration
  • VelocityTracker

在这里插入图片描述

部分答案展示:

View基础(25题)

1、简述View的绘制流程

2、onDraw()的绘制顺序

3、requestLayout()的作用

4、requestLayout在什么情况下只会触发测量和布局,而不会触发绘制

5、invalidate()的作用

View三大流程(28题)

1、ViewRoot如何完成View的三大流程?

2、View三大流程的作用?(3)

3、什么时候测量宽高不等于实际宽高?

measure过程

View

4、View的measure方法的特点?

5、View的onMeasure源码要点

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//1. setMeasuredDimension方法设置View宽/高的测量值
setMeasuredDimension(
//2. 第一个参数是获得的测量宽/高(通过getDefaultSize获取)
getDefaultSize(getSuggestedMinimumWidth(), //3. 获取的建议最小的宽/高
widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));
}

自定义View(26题)

四种实现方法

1、自定义View实现方法的分类?

分类注意点1注意点2注意点3
1.继承View重写onDraw()—绘制和支持padding重写onMeasure()—解决wrap_content问题
2.继承ViewGroup重写onMesaure()—测量子元素,测量自身,并且需要处理子View的margin和自身的padding必须实现onLayout()—布局子元素,并且处理子View的margin和自身的padding属性实现自身的LayoutParams并且重写LayoutParmas相关的3个方法—让子View的Margin属性生效
3.继承特定的View(TextView等)扩展较容易实现不需要额外支持wrap_contentpadding
4.继承特定的ViewGroup(LinearLayout等)方法2能实现的效果方法4都能实现

2、自定义View的注意点?(5)

直接继承View

3、直接继承自View的实现步骤和方法:

class CustomViewByView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int):
View(context, attrs, defStyleAttr, defStyleRes){
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int):this(context, attrs, defStyleAttr, 0)
constructor(context: Context, attrs: AttributeSet):this(context, attrs, 0, 0)
constructor(context: Context): this(context, null, 0, 0)

var mColor = Color.RED

init {
//3. 自定义attrs中属性的获取
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomViewByView)
mColor = typedArray.getColor(R.styleable.CustomViewByView_circle_color, Color.RED)
typedArray.recycle()
}

//1. 重写onDraw方法
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = mColor //属性attrs给定的颜色
//2. 需要处理padding
val width = width - paddingLeft - paddingRight
val height = height - paddingTop - paddingBottom
canvas.drawCircle(paddingLeft + width.toFloat() / 2, paddingTop + height.toFloat() / 2,
Math.min(width, height).toFloat() / 2, paint)
}

//3. 特别处理wrap_content的情况,给定一个最小值
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSpecSize = MeasureSpec.getSize(heightMeasureSpec)
when{
// 为wrap_content的边均使用最小值mMinWidth/mMinHeight
widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST -> {
setMeasuredDimension(minimumWidth, minimumHeight)
}
widthSpecMode == MeasureSpec.AT_MOST -> {
setMeasuredDimension(minimumWidth, heightSpecSize)
}
heightSpecMode == MeasureSpec.AT_MOST -> {
setMeasuredDimension(widthSpecSize, minimumHeight)
}
}
}
}

事件分发机制(22题)

1、事件分发

三个重要方法

2、简述Android的事件分发机制

dispatchTouchEvent

3、dispatchTouchEvent的作用

4、ViewGroup事件分发伪代码:

public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume = false;
    boolean intercepted = false;

    intercepted = onInterceptTouchEvent(ev);
    // 1、没有被拦截,分发给子View
    if(intercepted == false){
        consume = child.dispatchTouchEvent(ev);
    }
    // 2、事件被拦截因此自己进行处理 || 子View没有消耗该事件因此自己进行处理
    if(intercepted == true || consume == false){
        // 3、交给当前View进行处理(调用的是View的dispatchTouchEvent,该方法就是处理事件,等效于onTouchEvent)
        consume = super.dispacthTouchEvent(ev);
    }

    return consume;
}

5、View事件分发伪代码:

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    // 1. 判断是否有OnTouchListener,返回true,则处理完成
    if (mOnTouchListener != null){
        result = mOnTouchListener.onTouch(this, event);
    }
    // 2. 事件没有被消耗。并且,如果有代理,会执行代理的onTouchEvent方法
    if (result == false && mTouchDelegate != null) {
        result = mTouchDelegate.onTouchEvent(event);
    }
    // 3. 事件没有被消耗。才会调用onTouchEvent
    if (result == false) {
        result = onTouchEvent(event);

        // 4. 接收到UP事件,就会执行OnClickListener的onClick方法
        if(MotionEvent.ACTION_UP == action && mOnClickListener != null){
            mOnClickListener.onClick(event);
        }
    }
    // 5. 返回事件处理的结果(是否消耗该事件)
    return result;
}

6、View和ViewGroup在dispatchTouchEvent上的区别

onInterceptTouchEvent

滑动冲突(8题)

滑动冲突的三种场景

1、滑动冲突的三种场景

滑动冲突处理原则和解决办法

2、 滑动冲突处理原则

3、 滑动冲突解决办法

外部拦截

4、外部拦截法要点

5、onClick方法生效的两个条件?

6、外部拦截,自定义ScrollView

//Kotlin
class CustomScrollView(context: Context,
                       attrs: AttributeSet?,
                       defStyleAttr: Int,
                       defStyleRes: Int): ScrollView(context, attrs, defStyleAttr, defStyleRes) {

    constructor(context: Context) : this(context, null, 0, 0)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0)

    var lastX: Int = 0
    var lastY: Int = 0

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {

        val curX = ev.x.toInt()
        val curY = ev.y.toInt()

        when(ev.action){
            ACTION_DOWN -> {
                parent.requestDisallowInterceptTouchEvent(true)
            }
            ACTION_MOVE -> {
                //如果是水平滑动则交给父容器处理
                if(Math.abs(curX - lastX) > Math.abs(curY - lastY)){
                    parent.requestDisallowInterceptTouchEvent(false)
                }
            }
            ACTION_UP -> null
            else -> null
        }
        lastX = curX
        lastY = curY
        return super.dispatchTouchEvent(ev)
    }
}

滑动(39题)

滑动的7种实现方法

1、View滑动的7种方法:

2、Layout实现滑动

/*================================*
* onTouchEvent-进行偏移计算,之后调用layout
*================================*/
 public boolean onTouchEvent(MotionEvent event) {
     float curX = event.getX(); //手指实时位置的X
     float curY = event.getY(); //Y
     switch(event.getAction()){
        case MotionEvent.ACTION_MOVE:
           int offsetX = (int)(curX - downX); //X偏移
           int offsetY = (int)(curY - downY); //Y偏移
    /**=============================================
     * 变化后的距离=getLeft(当前控件距离父控件左边的距离)+偏移量——调用layout重新布局
     *============================================*/
           layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
           break;
        case MotionEvent.ACTION_DOWN:
           downX = curX; //按下时的坐标
           downY = curY;
           break;
     }
     return true;
 }

3、offsetLeftAndRight和offsetTopAndBottom实现滑动

/*================================*
* onTouchEvent-进行偏移计算,直接调用
*================================*/
 public boolean onTouchEvent(MotionEvent event) {
     float curX = event.getX(); //手指实时位置的X
     float curY = event.getY(); //Y
     switch(event.getAction()){
        case MotionEvent.ACTION_MOVE:
           int offsetX = (int)(curX - downX); //X偏移
           int offsetY = (int)(curY - downY); //Y偏移
     /**=============================================
      * 对left和right, top和bottom同时偏移
      *============================================*/
           offsetLeftAndRight(offsetX);
           offsetTopAndBottom(offsetY);
           break;
        case MotionEvent.ACTION_DOWN:
           downX = curX; //按下时的坐标
           downY = curY;
           break;
     }
     return true;
 }

4、LayoutParams实现滑动:

//方法一:通过布局设置在父控件的位置。但是必须要有父控件, 而且要指定父布局的类型,不好的方法。
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);

/**===============================================
 * 方法二:用ViewGroup的MarginLayoutParams的方法去设置marign
 * 优点:相比于上面方法, 就不需要知道父布局的类型。
 * 缺点:滑动到右侧控件会缩小
 *===============================================*/
ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
mlayoutParams.leftMargin = getLeft() + offsetX;
mlayoutParams.topMargin = getTop() + offsetY;
setLayoutParams(mlayoutParams);

5、scrollTo\scrollBy实现滑动

// 1、移动到目标位置
((View)getParent()).scrollTo(dstX, dstY);
// 2、相对滑动:且scrollBy是父容器进行滑动,因此偏移量需要取负
((View)getParent()).scrollBy(-offsetX, -offsetY);

6、scrollTo/By内部的mScrollX和mScrollY的意义

7、动画实现滑动的方法

8、ViewDragHelper

举报

相关推荐

0 条评论